UNPKG

graphql-shield

Version:

GraphQL Server permissions as another layer of abstraction!

200 lines (199 loc) 6.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateMiddlewareFromSchemaAndRuleTree = void 0; const graphql_1 = require("graphql"); const utils_js_1 = require("./utils.js"); const validation_js_1 = require("./validation.js"); /** * * @param options * * Generates a middleware function from a given rule and * initializes the cache object in context. * */ function generateFieldMiddlewareFromRule(rule, options) { async function middleware(resolve, parent, args, ctx, info) { // Cache if (!ctx) { ctx = {}; } if (!ctx._shield) { ctx._shield = { cache: {}, }; } // Execution try { const res = await rule.resolve(parent, args, ctx, info, options); if (res === true) { const result = await resolve(parent, args, ctx, info); if (result instanceof Error) { throw result; } return result; } else if (res === false) { if (typeof options.fallbackError === 'function') { return await options.fallbackError(null, parent, args, ctx, info); } return options.fallbackError; } else { return res; } } catch (err) { if (options.debug) { throw err; } else if (options.allowExternalErrors) { return err; } else { if (typeof options.fallbackError === 'function') { return await options.fallbackError(err, parent, args, ctx, info); } return options.fallbackError; } } } if ((0, utils_js_1.isRule)(rule) && rule.extractFragment()) { return { fragment: rule.extractFragment(), resolve: middleware, }; } if ((0, utils_js_1.isLogicRule)(rule)) { return { fragments: rule.extractFragments(), resolve: middleware, }; } return { resolve: middleware, }; } /** * * @param type * @param rules * @param options * * Generates middleware from rule for a particular type. * */ function applyRuleToType(type, rules, options) { if ((0, utils_js_1.isRuleFunction)(rules)) { /* Apply defined rule function to every field */ const fieldMap = type.getFields(); const middleware = Object.keys(fieldMap).reduce((middleware, field) => { return { ...middleware, [field]: generateFieldMiddlewareFromRule(rules, options), }; }, {}); return middleware; } else if ((0, utils_js_1.isRuleFieldMap)(rules)) { /* Apply rules assigned to each field to each field */ const fieldMap = type.getFields(); /* Extract default type wildcard if any and remove it for validation */ const { '*': defaultTypeRule, ...rulesWithoutWildcard } = rules; /* Validation */ const fieldErrors = Object.keys(rulesWithoutWildcard) .filter((type) => !Object.prototype.hasOwnProperty.call(fieldMap, type)) .map((field) => `${type.name}.${field}`) .join(', '); if (fieldErrors.length > 0) { throw new validation_js_1.ValidationError(`It seems like you have applied rules to ${fieldErrors} fields but Shield cannot find them in your schema.`); } /* Generation */ const middleware = Object.keys(fieldMap).reduce((middleware, field) => ({ ...middleware, [field]: generateFieldMiddlewareFromRule((0, utils_js_1.withDefault)(defaultTypeRule || options.fallbackRule)(rules[field]), options), }), {}); return middleware; } else { /* Apply fallbackRule to type with no defined rule */ const fieldMap = type.getFields(); const middleware = Object.keys(fieldMap).reduce((middleware, field) => ({ ...middleware, [field]: generateFieldMiddlewareFromRule(options.fallbackRule, options), }), {}); return middleware; } } /** * * @param schema * @param rule * @param options * * Applies the same rule over entire schema. * */ function applyRuleToSchema(schema, rule, options) { const typeMap = schema.getTypeMap(); const middleware = Object.keys(typeMap) .filter((type) => !(0, graphql_1.isIntrospectionType)(typeMap[type])) .reduce((middleware, typeName) => { const type = typeMap[typeName]; if ((0, graphql_1.isObjectType)(type)) { return { ...middleware, [typeName]: applyRuleToType(type, rule, options), }; } else { return middleware; } }, {}); return middleware; } /** * * @param rules * @param wrapper * * Converts rule tree to middleware. * */ function generateMiddlewareFromSchemaAndRuleTree(schema, rules, options) { if ((0, utils_js_1.isRuleFunction)(rules)) { /* Applies rule to entire schema. */ return applyRuleToSchema(schema, rules, options); } else { /** * Checks type map and field map and applies rules * to particular fields. */ const typeMap = schema.getTypeMap(); /* Validation */ const typeErrors = Object.keys(rules) .filter((type) => !Object.prototype.hasOwnProperty.call(typeMap, type)) .join(', '); if (typeErrors.length > 0) { throw new validation_js_1.ValidationError(`It seems like you have applied rules to ${typeErrors} types but Shield cannot find them in your schema.`); } // Generation const middleware = Object.keys(typeMap) .filter((type) => !(0, graphql_1.isIntrospectionType)(typeMap[type])) .reduce((middleware, typeName) => { const type = typeMap[typeName]; if ((0, graphql_1.isObjectType)(type)) { return { ...middleware, [typeName]: applyRuleToType(type, rules[typeName], options), }; } else { return middleware; } }, {}); return middleware; } } exports.generateMiddlewareFromSchemaAndRuleTree = generateMiddlewareFromSchemaAndRuleTree;