graphql-shield-generator
Version:
Emits a GraphQL Shield from your GraphQL schema
163 lines • 6.73 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.getOutputPath = exports.getTypeResolverMap = exports.constructShield = void 0;
const tslib_1 = require("tslib");
const schema_1 = require("@graphql-tools/schema");
const utils_1 = require("@graphql-tools/utils");
const graphql_1 = require("graphql");
const path_1 = tslib_1.__importDefault(require("path"));
const process_1 = require("process");
const constructShield = ({ typeResolverMap, options }) => {
let rootItems = '';
// Sort types by priority if groupbyobjects is enabled
const typeEntries = Object.entries(typeResolverMap);
const sortedTypes = options.groupbyobjects
? typeEntries.sort(([typeA], [typeB]) => {
const priority = { Query: 1, Mutation: 2, Subscription: 3 };
const priorityA = priority[typeA] || 99;
const priorityB = priority[typeB] || 99;
if (priorityA !== priorityB)
return priorityA - priorityB;
return typeA.localeCompare(typeB);
})
: typeEntries;
for (const [type, resolverNames] of sortedTypes) {
if (type.length > 0) {
const sortedResolvers = resolverNames.sort().map((resolverName) => { var _a; return `'${resolverName}': ${(_a = options.customrule) !== null && _a !== void 0 ? _a : 'allow'}`; });
const subscriptionLinesWrapped = `${type}: ${wrapWithObject({
shieldItemLines: sortedResolvers,
options,
})},`;
rootItems += subscriptionLinesWrapped;
}
}
if (rootItems.length === 0)
return '';
let shieldText = getImports('graphql-shield', options);
shieldText += '\n\n';
shieldText += wrapWithExport({
shieldObjectText: wrapWithGraphqlShieldCall({
shieldObjectTextWrapped: wrapWithObject({ shieldItemLines: rootItems, options }),
}, options),
options,
});
return shieldText;
};
exports.constructShield = constructShield;
const getTypeResolverMap = async (schema) => {
var _a;
if (!(schema instanceof graphql_1.GraphQLSchema)) {
schema = await (0, schema_1.makeExecutableSchema)({ typeDefs: schema.typeDefs, resolvers: schema.resolvers });
}
const typeResolverMap = {};
const rootTypeMap = (0, utils_1.getRootTypeMap)(schema);
for (const [, rootType] of rootTypeMap.entries()) {
const typeName = rootType.name;
const fields = rootType.getFields();
for (const [resolverName] of Object.entries(fields)) {
if (!typeResolverMap[typeName]) {
typeResolverMap[typeName] = [];
}
if (typeName === 'Query') {
typeResolverMap[typeName].push(resolverName);
}
else if (typeName === 'Mutation') {
typeResolverMap[typeName].push(resolverName);
}
else if (typeName === 'Subscription') {
typeResolverMap[typeName].push(resolverName);
}
}
}
const typeMap = schema.getTypeMap();
for (const typeName in typeMap) {
const type = typeMap[typeName];
if (((_a = type.astNode) === null || _a === void 0 ? void 0 : _a.kind) === 'ObjectTypeDefinition' && !['Query', 'Mutation', 'Subscription'].includes(type.name)) {
const foundType = schema.getType(type.name);
//@ts-ignore
const fields = foundType === null || foundType === void 0 ? void 0 : foundType.toConfig().fields;
Object.keys(fields).forEach((fieldName) => {
const field = fields[fieldName];
const resolver = field.resolve;
if (resolver) {
if (!typeResolverMap[type.name]) {
typeResolverMap[type.name] = [];
}
typeResolverMap[type.name].push(fieldName);
}
});
}
}
return typeResolverMap;
};
exports.getTypeResolverMap = getTypeResolverMap;
const getOutputPath = (options) => {
var _a, _b;
const ext = `.${(_a = options.extension) !== null && _a !== void 0 ? _a : 'js'}`;
const dirPath = options.outputDir ? path_1.default.resolve((0, process_1.cwd)(), options.outputDir) : path_1.default.join((0, process_1.cwd)());
const filePath = path_1.default.format({
dir: dirPath,
name: (_b = options.fileName) !== null && _b !== void 0 ? _b : 'shield',
ext: ext,
});
return filePath;
};
exports.getOutputPath = getOutputPath;
const wrapWithObject = ({ shieldItemLines, options }) => {
let wrapped = '{';
wrapped += '\n';
if (Array.isArray(shieldItemLines)) {
const fallbackRule = options === null || options === void 0 ? void 0 : options.fallbackRule;
const fallbackLine = fallbackRule ? ` '*': ${fallbackRule},\n` : '';
wrapped += fallbackLine + ' ' + shieldItemLines.join(',\n ');
}
else {
wrapped += ' ' + shieldItemLines;
}
wrapped += '\n';
wrapped += '}';
return wrapped;
};
const getImports = (type, options) => {
const needsDeny = options.fallbackRule === 'deny';
const needsAllow = !options.customrule || options.fallbackRule === 'allow';
const imports = ['shield'];
if (needsAllow)
imports.push('allow');
if (needsDeny)
imports.push('deny');
switch (options.moduleSystem) {
case 'ES modules':
let result = `import { ${imports.join(', ')} } from '${type}';\n`;
if (options.customrule) {
result += `import { ${options.customrule} } from '${options.customrulepath}';\n`;
}
return result;
case 'CommonJS':
default:
let resultCommon = `const { ${imports.join(', ')} } = require('${type}');\n`;
if (options.customrule) {
resultCommon += `const { ${options.customrule} } = require('${options.customrulepath}');\n`;
}
return resultCommon;
}
};
const wrapWithExport = ({ shieldObjectText, options }) => {
switch (options.moduleSystem) {
case 'ES modules':
return ` export const permissions = ${shieldObjectText};`;
case 'CommonJS':
default:
return ` module.exports.permissions = ${shieldObjectText};`;
}
};
const wrapWithGraphqlShieldCall = ({ shieldObjectTextWrapped }, options) => {
var _a;
let wrapped = 'shield(';
wrapped += '\n';
wrapped += ' ' + shieldObjectTextWrapped;
wrapped += `\n, ${(_a = options.shieldoptions) !== null && _a !== void 0 ? _a : ''}`;
wrapped += ')';
return wrapped;
};
//# sourceMappingURL=helpers.js.map
;