2018-09-10 16:05:00 +08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* GraphQL.js service
|
|
|
|
*
|
|
|
|
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const { gql, makeExecutableSchema } = require('apollo-server-koa');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const graphql = require('graphql');
|
|
|
|
const Types = require('./Types.js');
|
|
|
|
const Resolvers = require('./Resolvers.js');
|
2020-01-31 17:40:02 +01:00
|
|
|
const { mergeSchemas, createDefaultSchema, diffResolvers } = require('./utils');
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-29 15:30:53 +01:00
|
|
|
const policyUtils = require('strapi-utils').policy;
|
|
|
|
const compose = require('koa-compose');
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
/**
|
|
|
|
* Receive an Object and return a string which is following the GraphQL specs.
|
|
|
|
*
|
|
|
|
* @return String
|
|
|
|
*/
|
|
|
|
|
|
|
|
const formatGQL = (fields, description = {}, model = {}, type = 'field') => {
|
|
|
|
const typeFields = JSON.stringify(fields, null, 2).replace(/['",]+/g, '');
|
|
|
|
|
|
|
|
const lines = typeFields.split('\n');
|
|
|
|
|
|
|
|
// Try to add description for field.
|
|
|
|
if (type === 'field') {
|
|
|
|
return lines
|
|
|
|
.map(line => {
|
|
|
|
if (['{', '}'].includes(line)) {
|
|
|
|
return '';
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const split = line.split(':');
|
|
|
|
const attribute = _.trim(split[0]);
|
|
|
|
const info =
|
|
|
|
(_.isString(description[attribute])
|
|
|
|
? description[attribute]
|
|
|
|
: _.get(description[attribute], 'description')) ||
|
|
|
|
_.get(model, `attributes.${attribute}.description`);
|
|
|
|
const deprecated =
|
|
|
|
_.get(description[attribute], 'deprecated') ||
|
|
|
|
_.get(model, `attributes.${attribute}.deprecated`);
|
|
|
|
|
|
|
|
// Snakecase an attribute when we find a dash.
|
|
|
|
if (attribute.indexOf('-') !== -1) {
|
|
|
|
line = ` ${_.snakeCase(attribute)}: ${_.trim(split[1])}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info) {
|
|
|
|
line = ` """\n ${info}\n """\n${line}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deprecated) {
|
|
|
|
line = `${line} @deprecated(reason: "${deprecated}")`;
|
|
|
|
}
|
|
|
|
|
|
|
|
return line;
|
|
|
|
})
|
|
|
|
.join('\n');
|
|
|
|
} else if (type === 'query' || type === 'mutation') {
|
2018-09-10 16:05:00 +08:00
|
|
|
return lines
|
|
|
|
.map((line, index) => {
|
2020-01-30 10:49:42 +01:00
|
|
|
if (['{', '}'].includes(line)) {
|
2018-09-10 16:05:00 +08:00
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const split = Object.keys(fields)[index - 1].split('(');
|
|
|
|
const attribute = _.trim(split[0]);
|
|
|
|
const info = _.get(description[attribute], 'description');
|
|
|
|
const deprecated = _.get(description[attribute], 'deprecated');
|
|
|
|
|
|
|
|
// Snakecase an attribute when we find a dash.
|
|
|
|
if (attribute.indexOf('-') !== -1) {
|
|
|
|
line = ` ${_.snakeCase(attribute)}(${_.trim(split[1])}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info) {
|
|
|
|
line = ` """\n ${info}\n """\n${line}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deprecated) {
|
|
|
|
line = `${line} @deprecated(reason: "${deprecated}")`;
|
|
|
|
}
|
|
|
|
|
2018-09-10 16:05:00 +08:00
|
|
|
return line;
|
|
|
|
})
|
|
|
|
.join('\n');
|
2020-01-30 10:49:42 +01:00
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
return lines
|
|
|
|
.map((line, index) => {
|
|
|
|
if ([0, lines.length - 1].includes(index)) {
|
|
|
|
return '';
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
return line;
|
|
|
|
})
|
|
|
|
.join('\n');
|
|
|
|
};
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
/**
|
|
|
|
* Retrieve description from variable and return a string which follow the GraphQL specs.
|
|
|
|
*
|
|
|
|
* @return String
|
|
|
|
*/
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const getDescription = (type, model = {}) => {
|
|
|
|
const format = '"""\n';
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const str = _.get(type, '_description') || _.get(model, 'info.description');
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
if (str) {
|
|
|
|
return `${format}${str}\n${format}`;
|
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
return '';
|
|
|
|
};
|
2020-01-27 10:30:52 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
/**
|
|
|
|
* Generate GraphQL schema.
|
|
|
|
*
|
|
|
|
* @return Schema
|
|
|
|
*/
|
2019-07-17 13:13:07 +02:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const generateSchema = () => {
|
|
|
|
const shadowCRUDEnabled = strapi.plugins.graphql.config.shadowCRUD !== false;
|
2020-01-29 15:30:53 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Generate type definition and query/mutation for models.
|
|
|
|
const shadowCRUD = shadowCRUDEnabled
|
|
|
|
? buildShadowCRUD()
|
|
|
|
: createDefaultSchema();
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const _schema = strapi.plugins.graphql.config._schema.graphql;
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Extract custom definition, query or resolver.
|
|
|
|
const { definition, query, mutation, resolver = {} } = _schema;
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Polymorphic.
|
|
|
|
const polymorphicSchema = Types.addPolymorphicUnionType(
|
|
|
|
definition + shadowCRUD.definition
|
|
|
|
);
|
2020-01-29 15:30:53 +01:00
|
|
|
|
2020-01-31 17:40:02 +01:00
|
|
|
const builtResolvers = _.merge(
|
|
|
|
{},
|
|
|
|
shadowCRUD.resolvers,
|
|
|
|
polymorphicSchema.resolvers
|
|
|
|
);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-31 17:40:02 +01:00
|
|
|
const extraResolvers = diffResolvers(_schema.resolver, builtResolvers);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-31 17:40:02 +01:00
|
|
|
const resolvers = _.merge({}, builtResolvers, buildResolvers(extraResolvers));
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Return empty schema when there is no model.
|
|
|
|
if (_.isEmpty(shadowCRUD.definition) && _.isEmpty(definition)) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-02-10 11:57:06 +01:00
|
|
|
const queryFields =
|
|
|
|
shadowCRUD.query &&
|
|
|
|
formatGQL(shadowCRUD.query, resolver.Query, null, 'query');
|
2020-01-30 10:49:42 +01:00
|
|
|
|
2020-02-10 11:57:06 +01:00
|
|
|
const mutationFields =
|
|
|
|
shadowCRUD.mutation &&
|
|
|
|
formatGQL(shadowCRUD.mutation, resolver.Mutation, null, 'mutation');
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-31 17:40:02 +01:00
|
|
|
const scalars = Types.getScalars();
|
|
|
|
|
|
|
|
Object.assign(resolvers, scalars);
|
|
|
|
const scalarDef = Object.keys(scalars)
|
|
|
|
.map(key => `scalar ${key}`)
|
|
|
|
.join('\n');
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Concatenate.
|
|
|
|
let typeDefs = `
|
2020-01-27 22:43:44 +01:00
|
|
|
${definition}
|
|
|
|
${shadowCRUD.definition}
|
|
|
|
${polymorphicSchema.definition}
|
|
|
|
|
|
|
|
${Types.addInput()}
|
|
|
|
|
|
|
|
type Query {
|
|
|
|
${queryFields}
|
|
|
|
${query}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Mutation {
|
|
|
|
${mutationFields}
|
|
|
|
${mutation}
|
|
|
|
}
|
|
|
|
|
2020-01-31 17:40:02 +01:00
|
|
|
${scalarDef}
|
2020-01-27 22:43:44 +01:00
|
|
|
`;
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// // Build schema.
|
|
|
|
if (!strapi.config.currentEnvironment.server.production) {
|
|
|
|
// Write schema.
|
|
|
|
const schema = makeExecutableSchema({
|
|
|
|
typeDefs,
|
|
|
|
resolvers,
|
|
|
|
});
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
writeGenerateSchema(graphql.printSchema(schema));
|
|
|
|
}
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Remove custom scalar (like Upload);
|
|
|
|
typeDefs = Types.removeCustomScalar(typeDefs, resolvers);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
return {
|
|
|
|
typeDefs: gql(typeDefs),
|
|
|
|
resolvers,
|
|
|
|
};
|
|
|
|
};
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
/**
|
|
|
|
* Save into a file the readable GraphQL schema.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const writeGenerateSchema = schema => {
|
|
|
|
return strapi.fs.writeAppFile('exports/graphql/schema.graphql', schema);
|
|
|
|
};
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const buildShadowCRUD = () => {
|
|
|
|
const modelSchema = Resolvers.buildShadowCRUD(
|
|
|
|
_.omitBy(strapi.models, model => model.internal === true)
|
|
|
|
);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const pluginSchemas = Object.keys(strapi.plugins).reduce((acc, plugin) => {
|
|
|
|
const schemas = Resolvers.buildShadowCRUD(strapi.plugins[plugin].models);
|
|
|
|
return acc.concat(schemas);
|
|
|
|
}, []);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const componentSchemas = Object.values(strapi.components).map(compo =>
|
|
|
|
Resolvers.buildComponent(compo)
|
|
|
|
);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const schema = { definition: '', resolvers: {}, query: {}, mutation: {} };
|
|
|
|
mergeSchemas(schema, modelSchema, ...pluginSchemas, ...componentSchemas);
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
return schema;
|
|
|
|
};
|
2020-01-27 22:43:44 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const buildResolvers = resolvers => {
|
|
|
|
// Transform object to only contain function.
|
2020-01-31 17:40:02 +01:00
|
|
|
return Object.keys(resolvers).reduce((acc, type) => {
|
|
|
|
if (graphql.isScalarType(resolvers[type])) {
|
2020-01-30 10:49:42 +01:00
|
|
|
return acc;
|
|
|
|
}
|
2019-11-28 15:33:37 +01:00
|
|
|
|
2020-01-31 17:40:02 +01:00
|
|
|
return Object.keys(resolvers[type]).reduce((acc, resolverName) => {
|
|
|
|
const resolverObj = resolvers[type][resolverName];
|
2020-01-29 15:30:53 +01:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
// Disabled this query.
|
2020-01-31 17:40:02 +01:00
|
|
|
if (resolverObj === false) return acc;
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
if (_.isFunction(resolverObj)) {
|
2020-01-31 17:40:02 +01:00
|
|
|
return _.set(acc, [type, resolverName], resolverObj);
|
2020-01-30 10:49:42 +01:00
|
|
|
}
|
2019-09-18 12:07:45 +02:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
switch (type) {
|
|
|
|
case 'Mutation': {
|
2020-01-31 17:40:02 +01:00
|
|
|
_.set(
|
|
|
|
acc,
|
|
|
|
[type, resolverName],
|
|
|
|
buildMutation(resolverName, resolverObj)
|
|
|
|
);
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
2020-01-31 17:40:02 +01:00
|
|
|
_.set(
|
|
|
|
acc,
|
|
|
|
[type, resolverName],
|
|
|
|
buildQuery(resolverName, resolverObj)
|
|
|
|
);
|
2020-01-30 10:49:42 +01:00
|
|
|
break;
|
2018-09-10 16:05:00 +08:00
|
|
|
}
|
2020-01-30 10:49:42 +01:00
|
|
|
}
|
2018-09-10 16:05:00 +08:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
return acc;
|
|
|
|
}, acc);
|
2020-01-31 17:40:02 +01:00
|
|
|
}, {});
|
2018-09-10 16:05:00 +08:00
|
|
|
};
|
2019-03-13 19:27:18 +01:00
|
|
|
|
2020-01-29 15:30:53 +01:00
|
|
|
// TODO: implement
|
|
|
|
const buildMutation = (mutationName, config) => {
|
|
|
|
const { resolver, resolverOf, transformOutput = _.identity } = config;
|
|
|
|
|
|
|
|
if (_.isFunction(resolver) && !isResolvablePath(resolverOf)) {
|
|
|
|
throw new Error(
|
|
|
|
`Cannot create mutation "${mutationName}". Missing "resolverOf" option with custom resolver.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const policiesMiddleware = compose(getPolicies(config));
|
|
|
|
|
|
|
|
// custom resolvers
|
|
|
|
if (_.isFunction(resolver)) {
|
|
|
|
return async (root, options = {}, graphqlContext) => {
|
|
|
|
const ctx = buildMutationContext({ options, graphqlContext });
|
|
|
|
|
|
|
|
await policiesMiddleware(ctx);
|
|
|
|
return resolver(root, options, graphqlContext);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const action = getAction(resolver);
|
|
|
|
|
|
|
|
return async (root, options = {}, graphqlContext) => {
|
|
|
|
const { context } = graphqlContext;
|
|
|
|
const ctx = buildMutationContext({ options, graphqlContext });
|
|
|
|
|
|
|
|
await policiesMiddleware(ctx);
|
|
|
|
|
|
|
|
const values = await action(context);
|
|
|
|
const result = ctx.body || values;
|
|
|
|
|
|
|
|
if (_.isError(result)) {
|
|
|
|
throw result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return transformOutput(result);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const buildMutationContext = ({ options, graphqlContext }) => {
|
|
|
|
const { context } = graphqlContext;
|
|
|
|
|
|
|
|
if (options.input && options.input.where) {
|
|
|
|
context.params = convertToParams(options.input.where || {});
|
|
|
|
} else {
|
|
|
|
context.params = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.input && options.input.data) {
|
|
|
|
context.request.body = options.input.data || {};
|
|
|
|
} else {
|
|
|
|
context.request.body = options;
|
|
|
|
}
|
|
|
|
|
|
|
|
const ctx = context.app.createContext(
|
|
|
|
_.clone(context.req),
|
|
|
|
_.clone(context.res)
|
|
|
|
);
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
};
|
|
|
|
|
|
|
|
const buildQuery = (queryName, config) => {
|
|
|
|
const { resolver, resolverOf } = config;
|
|
|
|
|
|
|
|
if (_.isFunction(resolver) && !isResolvablePath(resolverOf)) {
|
|
|
|
throw new Error(
|
|
|
|
`Cannot create query "${queryName}". Missing "resolverOf" option with custom resolver.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const policiesMiddleware = compose(getPolicies(config));
|
|
|
|
|
|
|
|
// custom resolvers
|
|
|
|
if (_.isFunction(resolver)) {
|
|
|
|
return async (root, options = {}, graphqlContext) => {
|
|
|
|
const { ctx, opts } = buildQueryContext({ options, graphqlContext });
|
|
|
|
|
|
|
|
await policiesMiddleware(ctx);
|
|
|
|
return resolver(root, opts, graphqlContext);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const action = getAction(resolver);
|
|
|
|
|
|
|
|
return async (root, options = {}, graphqlContext) => {
|
|
|
|
const { ctx } = buildQueryContext({ options, graphqlContext });
|
|
|
|
|
|
|
|
// duplicate context
|
|
|
|
await policiesMiddleware(ctx);
|
|
|
|
|
|
|
|
const values = await action(ctx);
|
|
|
|
const result = ctx.body || values;
|
|
|
|
|
|
|
|
if (_.isError(result)) {
|
|
|
|
throw result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const buildQueryContext = ({ options, graphqlContext }) => {
|
|
|
|
const { context } = graphqlContext;
|
|
|
|
const _options = _.cloneDeep(options);
|
|
|
|
|
|
|
|
const ctx = context.app.createContext(
|
|
|
|
_.clone(context.req),
|
|
|
|
_.clone(context.res)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Note: we've to used the Object.defineProperties to reset the prototype. It seems that the cloning the context
|
|
|
|
// cause a lost of the Object prototype.
|
|
|
|
const opts = amountLimiting(_options);
|
|
|
|
|
|
|
|
ctx.query = {
|
|
|
|
...convertToParams(_.omit(opts, 'where')),
|
|
|
|
...convertToQuery(opts.where),
|
|
|
|
};
|
|
|
|
|
|
|
|
ctx.params = convertToParams(opts);
|
|
|
|
|
|
|
|
return { ctx, opts };
|
|
|
|
};
|
|
|
|
|
|
|
|
const convertToParams = params => {
|
|
|
|
return Object.keys(params).reduce((acc, current) => {
|
|
|
|
const key = current === 'id' ? 'id' : `_${current}`;
|
|
|
|
acc[key] = params[current];
|
|
|
|
return acc;
|
|
|
|
}, {});
|
|
|
|
};
|
|
|
|
|
|
|
|
const convertToQuery = params => {
|
|
|
|
const result = {};
|
|
|
|
|
|
|
|
_.forEach(params, (value, key) => {
|
|
|
|
if (_.isPlainObject(value)) {
|
|
|
|
const flatObject = convertToQuery(value);
|
|
|
|
_.forEach(flatObject, (_value, _key) => {
|
|
|
|
result[`${key}.${_key}`] = _value;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
result[key] = value;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
const amountLimiting = (params = {}) => {
|
|
|
|
const { amountLimit } = strapi.plugins.graphql.config;
|
|
|
|
|
|
|
|
if (!amountLimit) return params;
|
|
|
|
|
|
|
|
if (!params.limit || params.limit === -1 || params.limit > amountLimit) {
|
|
|
|
params.limit = amountLimit;
|
|
|
|
} else if (params.limit < 0) {
|
|
|
|
params.limit = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return params;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getAction = resolver => {
|
|
|
|
if (!_.isString(resolver)) {
|
|
|
|
throw new Error(`Error building query. Expected a string, got ${resolver}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const { controller, action, plugin, api } = getActionDetails(resolver);
|
|
|
|
|
|
|
|
let fn;
|
|
|
|
|
|
|
|
if (plugin) {
|
|
|
|
fn = _.get(strapi.plugins, [plugin, 'controllers', controller, action]);
|
|
|
|
} else {
|
|
|
|
fn = _.get(strapi.api, [api, 'controllers', controller, action]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fn) {
|
|
|
|
throw new Error(`Cannot find action ${resolver}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fn;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getActionDetails = resolver => {
|
|
|
|
if (resolver.startsWith('plugins::')) {
|
|
|
|
const [, path] = resolver.split('::');
|
|
|
|
const [plugin, controller, action] = path.split('.');
|
|
|
|
|
|
|
|
return { plugin, controller, action };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resolver.startsWith('application::')) {
|
|
|
|
const [, path] = resolver.split('::');
|
|
|
|
const [api, controller, action] = path.split('.');
|
|
|
|
|
|
|
|
return { api, controller, action };
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to find legacy stuff
|
|
|
|
_.get(strapi.plugins);
|
|
|
|
|
|
|
|
throw new Error('Could not parse resolverString: ', resolver);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if a resolverPath (resolver or resovlerOf) might be resolved
|
|
|
|
*/
|
|
|
|
const isResolvablePath = path => _.isString(path) && !_.isEmpty(path);
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const getPolicies = config => {
|
2020-01-29 15:30:53 +01:00
|
|
|
const { resolver, policies = [], resolverOf } = config;
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const { api, plugin } = config['_metadatas'] || {};
|
|
|
|
|
2020-01-29 15:30:53 +01:00
|
|
|
const policyFns = [];
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const { controller, action, plugin: pathPlugin } = isResolvablePath(
|
|
|
|
resolverOf
|
|
|
|
)
|
2020-01-29 15:30:53 +01:00
|
|
|
? getActionDetails(resolverOf)
|
|
|
|
: getActionDetails(resolver);
|
|
|
|
|
|
|
|
const globalPolicy = policyUtils.globalPolicy({
|
|
|
|
controller,
|
|
|
|
action,
|
2020-01-30 10:49:42 +01:00
|
|
|
plugin: pathPlugin,
|
2020-01-29 15:30:53 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
policyFns.push(globalPolicy);
|
|
|
|
|
|
|
|
if (strapi.plugins['users-permissions']) {
|
|
|
|
policies.unshift('plugins.users-permissions.permissions');
|
|
|
|
}
|
|
|
|
|
|
|
|
policies.forEach(policy => {
|
|
|
|
const policyFn = policyUtils.get(policy, plugin, api);
|
|
|
|
policyFns.push(policyFn);
|
|
|
|
});
|
|
|
|
|
|
|
|
return policyFns;
|
|
|
|
};
|
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
module.exports = {
|
|
|
|
generateSchema,
|
|
|
|
getDescription,
|
|
|
|
formatGQL,
|
|
|
|
buildQuery,
|
2020-01-31 17:40:02 +01:00
|
|
|
buildMutation,
|
2020-01-30 10:49:42 +01:00
|
|
|
};
|