mirror of
https://github.com/strapi/strapi.git
synced 2025-11-15 09:39:15 +00:00
Add metadatas to resolvers to know where they are created
Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
parent
0c6d39297f
commit
0285c7bd96
@ -24,7 +24,7 @@ module.exports = {
|
|||||||
resolver: 'application::homepage.homepage.find',
|
resolver: 'application::homepage.homepage.find',
|
||||||
},
|
},
|
||||||
q1: {
|
q1: {
|
||||||
policies: ['homepage.test'],
|
policies: ['test'],
|
||||||
resolverOf: 'application::restaurant.restaurant.find',
|
resolverOf: 'application::restaurant.restaurant.find',
|
||||||
resolver(root, args, ctx) {
|
resolver(root, args, ctx) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"amountLimit": 5
|
"amountLimit": 5,
|
||||||
|
"depthLimit": 10
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,26 @@ const { ApolloServer } = require('apollo-server-koa');
|
|||||||
const depthLimit = require('graphql-depth-limit');
|
const depthLimit = require('graphql-depth-limit');
|
||||||
const loadConfigs = require('./load-config');
|
const loadConfigs = require('./load-config');
|
||||||
|
|
||||||
|
const attachMetadataToResolvers = (schema, { api, plugin }) => {
|
||||||
|
const { resolver = {} } = schema;
|
||||||
|
if (_.isEmpty(resolver)) return schema;
|
||||||
|
|
||||||
|
Object.keys(resolver).forEach(type => {
|
||||||
|
if (!_.isPlainObject(resolver[type])) return;
|
||||||
|
|
||||||
|
Object.keys(resolver[type]).forEach(resolverName => {
|
||||||
|
if (!_.isPlainObject(resolver[type][resolverName])) return;
|
||||||
|
|
||||||
|
resolver[type][resolverName]['_metadatas'] = {
|
||||||
|
api,
|
||||||
|
plugin,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = strapi => {
|
module.exports = strapi => {
|
||||||
const { appPath, installedPlugins } = strapi.config;
|
const { appPath, installedPlugins } = strapi.config;
|
||||||
|
|
||||||
@ -32,23 +52,32 @@ module.exports = strapi => {
|
|||||||
/*
|
/*
|
||||||
* Create a merge of all the GraphQL configuration.
|
* Create a merge of all the GraphQL configuration.
|
||||||
*/
|
*/
|
||||||
const apisSchemas = Object.keys(strapi.api || {}).map(key =>
|
const apisSchemas = Object.keys(strapi.api || {}).map(key => {
|
||||||
_.get(strapi.api[key], 'config.schema.graphql', {})
|
const schema = _.get(strapi.api[key], 'config.schema.graphql', {});
|
||||||
);
|
return attachMetadataToResolvers(schema, { api: key });
|
||||||
|
});
|
||||||
|
|
||||||
const pluginsSchemas = Object.keys(strapi.plugins || {}).map(key =>
|
const pluginsSchemas = Object.keys(strapi.plugins || {}).map(key => {
|
||||||
_.get(strapi.plugins[key], 'config.schema.graphql', {})
|
const schema = _.get(strapi.plugins[key], 'config.schema.graphql', {});
|
||||||
);
|
return attachMetadataToResolvers(schema, { plugin: key });
|
||||||
|
});
|
||||||
|
|
||||||
const extensionsSchemas = Object.keys(extensions || {}).map(key =>
|
const extensionsSchemas = Object.keys(extensions || {}).map(key => {
|
||||||
_.get(extensions[key], 'config.schema.graphql', {})
|
const schema = _.get(extensions[key], 'config.schema.graphql', {});
|
||||||
);
|
return attachMetadataToResolvers(schema, { plugin: key });
|
||||||
|
});
|
||||||
|
|
||||||
|
const baseSchema = mergeSchemas([
|
||||||
|
...apisSchemas,
|
||||||
|
...pluginsSchemas,
|
||||||
|
...extensionsSchemas,
|
||||||
|
]);
|
||||||
|
|
||||||
// save the final schema in the plugin's config
|
// save the final schema in the plugin's config
|
||||||
_.set(
|
_.set(
|
||||||
strapi,
|
strapi,
|
||||||
['plugins', 'graphql', 'config', '_schema', 'graphql'],
|
['plugins', 'graphql', 'config', '_schema', 'graphql'],
|
||||||
mergeSchemas([...apisSchemas, ...pluginsSchemas, ...extensionsSchemas])
|
baseSchema
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ const loadPluginsGraphqlConfig = async installedPlugins => {
|
|||||||
pluginDir,
|
pluginDir,
|
||||||
'config/*.graphql?(.js)'
|
'config/*.graphql?(.js)'
|
||||||
);
|
);
|
||||||
|
|
||||||
_.set(root, ['plugins', pluginName], result);
|
_.set(root, ['plugins', pluginName], result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,6 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const pluralize = require('pluralize');
|
const pluralize = require('pluralize');
|
||||||
const { convertRestQueryParams, buildQuery } = require('strapi-utils');
|
const { convertRestQueryParams, buildQuery } = require('strapi-utils');
|
||||||
const policyUtils = require('strapi-utils').policy;
|
|
||||||
const compose = require('koa-compose');
|
|
||||||
|
|
||||||
const Schema = require('./Schema.js');
|
const Schema = require('./Schema.js');
|
||||||
const GraphQLQuery = require('./Query.js');
|
const GraphQLQuery = require('./Query.js');
|
||||||
@ -204,14 +202,15 @@ const preProcessGroupByData = function({ result, fieldKey, filters, model }) {
|
|||||||
const _result = _.toArray(result);
|
const _result = _.toArray(result);
|
||||||
return _.map(_result, value => {
|
return _.map(_result, value => {
|
||||||
return {
|
return {
|
||||||
key: value._id,
|
key: value._id.toString(),
|
||||||
connection: () => {
|
connection: () => {
|
||||||
// filter by the grouped by value in next connection
|
// filter by the grouped by value in next connection
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...filters,
|
...filters,
|
||||||
where: {
|
where: {
|
||||||
...(filters.where || {}),
|
...(filters.where || {}),
|
||||||
[fieldKey]: value._id,
|
[fieldKey]: value._id.toString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -256,7 +255,7 @@ const createGroupByFieldsResolver = function(model, fields, name) {
|
|||||||
filters: convertRestQueryParams(params),
|
filters: convertRestQueryParams(params),
|
||||||
aggregate: true,
|
aggregate: true,
|
||||||
}).group({
|
}).group({
|
||||||
_id: `$${fieldKey}`,
|
_id: `$${fieldKey === 'id' ? model.primaryKey : fieldKey}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
return preProcessGroupByData({
|
return preProcessGroupByData({
|
||||||
@ -476,14 +475,13 @@ const formatModelConnectionsGQL = function({
|
|||||||
fields,
|
fields,
|
||||||
model,
|
model,
|
||||||
name,
|
name,
|
||||||
rootQuery,
|
resolver,
|
||||||
plugin,
|
plugin,
|
||||||
}) {
|
}) {
|
||||||
const { globalId } = model;
|
const { globalId } = model;
|
||||||
|
|
||||||
const _schema = strapi.plugins.graphql.config._schema.graphql;
|
|
||||||
|
|
||||||
const connectionGlobalId = `${globalId}Connection`;
|
const connectionGlobalId = `${globalId}Connection`;
|
||||||
|
|
||||||
const aggregatorFormat = formatConnectionAggregator(fields, model, name);
|
const aggregatorFormat = formatConnectionAggregator(fields, model, name);
|
||||||
const groupByFormat = formatConnectionGroupBy(fields, model, name);
|
const groupByFormat = formatConnectionGroupBy(fields, model, name);
|
||||||
const connectionFields = {
|
const connectionFields = {
|
||||||
@ -503,16 +501,9 @@ const formatModelConnectionsGQL = function({
|
|||||||
|
|
||||||
const queryName = `${pluralName}Connection(sort: String, limit: Int, start: Int, where: JSON)`;
|
const queryName = `${pluralName}Connection(sort: String, limit: Int, start: Int, where: JSON)`;
|
||||||
|
|
||||||
const policiesFn = [
|
const connectionResolver = Schema.buildQuery(
|
||||||
policyUtils.globalPolicy({
|
`${pluralName}Connection.values`,
|
||||||
controller: name,
|
resolver
|
||||||
action: 'find',
|
|
||||||
plugin,
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
policiesFn.push(
|
|
||||||
policyUtils.get('plugins.users-permissions.permissions', plugin, name)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -523,25 +514,16 @@ const formatModelConnectionsGQL = function({
|
|||||||
},
|
},
|
||||||
resolvers: {
|
resolvers: {
|
||||||
Query: {
|
Query: {
|
||||||
async [`${pluralName}Connection`](obj, options, { context }) {
|
[`${pluralName}Connection`]: {
|
||||||
// need to check
|
resolverOf: resolver.resolverOf || resolver.resolver,
|
||||||
const ctx = context.app.createContext(
|
resolver(obj, options, { context }) {
|
||||||
_.clone(context.req),
|
return options;
|
||||||
_.clone(context.res)
|
},
|
||||||
);
|
|
||||||
|
|
||||||
await compose(policiesFn)(ctx);
|
|
||||||
return options;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[connectionGlobalId]: {
|
[connectionGlobalId]: {
|
||||||
values(obj, options, context) {
|
values(obj, options, gqlCtx) {
|
||||||
// use base resolver
|
return connectionResolver(obj, obj, gqlCtx);
|
||||||
return _.get(_schema, ['resolver', 'Query', rootQuery])(
|
|
||||||
obj,
|
|
||||||
obj,
|
|
||||||
context
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
groupBy(obj, options, context) {
|
groupBy(obj, options, context) {
|
||||||
return obj;
|
return obj;
|
||||||
|
|||||||
@ -424,6 +424,7 @@ const buildCollectionType = model => {
|
|||||||
Query: {
|
Query: {
|
||||||
[singularName]: {
|
[singularName]: {
|
||||||
resolver: `${model.uid}.findOne`,
|
resolver: `${model.uid}.findOne`,
|
||||||
|
..._.get(_schema, `resolver.Query.${singularName}`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -431,7 +432,7 @@ const buildCollectionType = model => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isQueryEnabled(_schema, pluralName)) {
|
if (isQueryEnabled(_schema, pluralName)) {
|
||||||
const resolverObj = {
|
const resolverOpt = {
|
||||||
resolver: `${model.uid}.find`,
|
resolver: `${model.uid}.find`,
|
||||||
..._.get(_schema, `resolver.Query.${pluralName}`),
|
..._.get(_schema, `resolver.Query.${pluralName}`),
|
||||||
};
|
};
|
||||||
@ -442,10 +443,24 @@ const buildCollectionType = model => {
|
|||||||
},
|
},
|
||||||
resolvers: {
|
resolvers: {
|
||||||
Query: {
|
Query: {
|
||||||
[pluralName]: resolverObj,
|
[pluralName]: resolverOpt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: Add support for Graphql Aggregation in Bookshelf ORM
|
||||||
|
if (model.orm === 'mongoose') {
|
||||||
|
// Generation the aggregation for the given model
|
||||||
|
const aggregationSchema = Aggregator.formatModelConnectionsGQL({
|
||||||
|
fields: typeDefObj,
|
||||||
|
model,
|
||||||
|
name: modelName,
|
||||||
|
resolver: resolverOpt,
|
||||||
|
plugin,
|
||||||
|
});
|
||||||
|
|
||||||
|
mergeSchemas(localSchema, aggregationSchema);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add model Input definition.
|
// Add model Input definition.
|
||||||
@ -458,20 +473,6 @@ const buildCollectionType = model => {
|
|||||||
mergeSchemas(localSchema, mutationScheam);
|
mergeSchemas(localSchema, mutationScheam);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Add support for Graphql Aggregation in Bookshelf ORM
|
|
||||||
if (model.orm === 'mongoose') {
|
|
||||||
// Generation the aggregation for the given model
|
|
||||||
const aggregationSchema = Aggregator.formatModelConnectionsGQL({
|
|
||||||
fields: typeDefObj,
|
|
||||||
model,
|
|
||||||
name: modelName,
|
|
||||||
rootQuery: pluralName,
|
|
||||||
plugin,
|
|
||||||
});
|
|
||||||
|
|
||||||
mergeSchemas(localSchema, aggregationSchema);
|
|
||||||
}
|
|
||||||
|
|
||||||
return localSchema;
|
return localSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -16,169 +16,167 @@ const { mergeSchemas, createDefaultSchema } = require('./utils');
|
|||||||
const policyUtils = require('strapi-utils').policy;
|
const policyUtils = require('strapi-utils').policy;
|
||||||
const compose = require('koa-compose');
|
const compose = require('koa-compose');
|
||||||
|
|
||||||
const schemaBuilder = {
|
/**
|
||||||
/**
|
* Receive an Object and return a string which is following the GraphQL specs.
|
||||||
* Receive an Object and return a string which is following the GraphQL specs.
|
*
|
||||||
*
|
* @return String
|
||||||
* @return String
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
formatGQL: function(fields, description = {}, model = {}, type = 'field') {
|
const formatGQL = (fields, description = {}, model = {}, type = 'field') => {
|
||||||
const typeFields = JSON.stringify(fields, null, 2).replace(/['",]+/g, '');
|
const typeFields = JSON.stringify(fields, null, 2).replace(/['",]+/g, '');
|
||||||
|
|
||||||
const lines = typeFields.split('\n');
|
const lines = typeFields.split('\n');
|
||||||
|
|
||||||
// Try to add description for field.
|
|
||||||
if (type === 'field') {
|
|
||||||
return lines
|
|
||||||
.map(line => {
|
|
||||||
if (['{', '}'].includes(line)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
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') {
|
|
||||||
return lines
|
|
||||||
.map((line, index) => {
|
|
||||||
if (['{', '}'].includes(line)) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
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}")`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return line;
|
|
||||||
})
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Try to add description for field.
|
||||||
|
if (type === 'field') {
|
||||||
return lines
|
return lines
|
||||||
.map((line, index) => {
|
.map(line => {
|
||||||
if ([0, lines.length - 1].includes(index)) {
|
if (['{', '}'].includes(line)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return line;
|
||||||
})
|
})
|
||||||
.join('\n');
|
.join('\n');
|
||||||
},
|
} else if (type === 'query' || type === 'mutation') {
|
||||||
|
return lines
|
||||||
|
.map((line, index) => {
|
||||||
|
if (['{', '}'].includes(line)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
const split = Object.keys(fields)[index - 1].split('(');
|
||||||
* Retrieve description from variable and return a string which follow the GraphQL specs.
|
const attribute = _.trim(split[0]);
|
||||||
*
|
const info = _.get(description[attribute], 'description');
|
||||||
* @return String
|
const deprecated = _.get(description[attribute], 'deprecated');
|
||||||
*/
|
|
||||||
|
|
||||||
getDescription: (type, model = {}) => {
|
// Snakecase an attribute when we find a dash.
|
||||||
const format = '"""\n';
|
if (attribute.indexOf('-') !== -1) {
|
||||||
|
line = ` ${_.snakeCase(attribute)}(${_.trim(split[1])}`;
|
||||||
|
}
|
||||||
|
|
||||||
const str = _.get(type, '_description') || _.get(model, 'info.description');
|
if (info) {
|
||||||
|
line = ` """\n ${info}\n """\n${line}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (str) {
|
if (deprecated) {
|
||||||
return `${format}${str}\n${format}`;
|
line = `${line} @deprecated(reason: "${deprecated}")`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return line;
|
||||||
},
|
})
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
return lines
|
||||||
* Generate GraphQL schema.
|
.map((line, index) => {
|
||||||
*
|
if ([0, lines.length - 1].includes(index)) {
|
||||||
* @return Schema
|
return '';
|
||||||
*/
|
}
|
||||||
|
|
||||||
generateSchema: function() {
|
return line;
|
||||||
const shadowCRUDEnabled =
|
})
|
||||||
strapi.plugins.graphql.config.shadowCRUD !== false;
|
.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
// Generate type definition and query/mutation for models.
|
/**
|
||||||
const shadowCRUD = shadowCRUDEnabled
|
* Retrieve description from variable and return a string which follow the GraphQL specs.
|
||||||
? this.buildShadowCRUD()
|
*
|
||||||
: createDefaultSchema();
|
* @return String
|
||||||
|
*/
|
||||||
|
|
||||||
const _schema = strapi.plugins.graphql.config._schema.graphql;
|
const getDescription = (type, model = {}) => {
|
||||||
|
const format = '"""\n';
|
||||||
|
|
||||||
// Extract custom definition, query or resolver.
|
const str = _.get(type, '_description') || _.get(model, 'info.description');
|
||||||
const { definition, query, mutation, resolver = {} } = _schema;
|
|
||||||
|
|
||||||
// Polymorphic.
|
if (str) {
|
||||||
const polymorphicSchema = Types.addPolymorphicUnionType(
|
return `${format}${str}\n${format}`;
|
||||||
definition + shadowCRUD.definition
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// Build resolvers.
|
return '';
|
||||||
const resolvers =
|
};
|
||||||
_.omitBy(
|
|
||||||
_.merge(shadowCRUD.resolvers, resolver, polymorphicSchema.resolvers),
|
|
||||||
_.isEmpty
|
|
||||||
) || {};
|
|
||||||
|
|
||||||
_schema.resolver = resolvers;
|
/**
|
||||||
|
* Generate GraphQL schema.
|
||||||
|
*
|
||||||
|
* @return Schema
|
||||||
|
*/
|
||||||
|
|
||||||
this.buildResolvers(resolvers);
|
const generateSchema = () => {
|
||||||
|
const shadowCRUDEnabled = strapi.plugins.graphql.config.shadowCRUD !== false;
|
||||||
|
|
||||||
// Return empty schema when there is no model.
|
// Generate type definition and query/mutation for models.
|
||||||
if (_.isEmpty(shadowCRUD.definition) && _.isEmpty(definition)) {
|
const shadowCRUD = shadowCRUDEnabled
|
||||||
return {};
|
? buildShadowCRUD()
|
||||||
}
|
: createDefaultSchema();
|
||||||
|
|
||||||
const queryFields = this.formatGQL(
|
const _schema = strapi.plugins.graphql.config._schema.graphql;
|
||||||
shadowCRUD.query,
|
|
||||||
resolver.Query,
|
|
||||||
null,
|
|
||||||
'query'
|
|
||||||
);
|
|
||||||
|
|
||||||
const mutationFields = this.formatGQL(
|
// Extract custom definition, query or resolver.
|
||||||
shadowCRUD.mutation,
|
const { definition, query, mutation, resolver = {} } = _schema;
|
||||||
resolver.Mutation,
|
|
||||||
null,
|
|
||||||
'mutation'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Concatenate.
|
// Polymorphic.
|
||||||
let typeDefs = `
|
const polymorphicSchema = Types.addPolymorphicUnionType(
|
||||||
|
definition + shadowCRUD.definition
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build resolvers.
|
||||||
|
const resolvers =
|
||||||
|
_.omitBy(
|
||||||
|
_.merge(shadowCRUD.resolvers, resolver, polymorphicSchema.resolvers),
|
||||||
|
_.isEmpty
|
||||||
|
) || {};
|
||||||
|
|
||||||
|
_schema.resolver = resolvers;
|
||||||
|
|
||||||
|
buildResolvers(resolvers);
|
||||||
|
|
||||||
|
// Return empty schema when there is no model.
|
||||||
|
if (_.isEmpty(shadowCRUD.definition) && _.isEmpty(definition)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryFields = formatGQL(
|
||||||
|
shadowCRUD.query,
|
||||||
|
resolver.Query,
|
||||||
|
null,
|
||||||
|
'query'
|
||||||
|
);
|
||||||
|
|
||||||
|
const mutationFields = formatGQL(
|
||||||
|
shadowCRUD.mutation,
|
||||||
|
resolver.Mutation,
|
||||||
|
null,
|
||||||
|
'mutation'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Concatenate.
|
||||||
|
let typeDefs = `
|
||||||
${definition}
|
${definition}
|
||||||
${shadowCRUD.definition}
|
${shadowCRUD.definition}
|
||||||
${polymorphicSchema.definition}
|
${polymorphicSchema.definition}
|
||||||
@ -198,93 +196,92 @@ const schemaBuilder = {
|
|||||||
${Types.addCustomScalar(resolvers)}
|
${Types.addCustomScalar(resolvers)}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// // Build schema.
|
// // Build schema.
|
||||||
if (!strapi.config.currentEnvironment.server.production) {
|
if (!strapi.config.currentEnvironment.server.production) {
|
||||||
// Write schema.
|
// Write schema.
|
||||||
const schema = makeExecutableSchema({
|
const schema = makeExecutableSchema({
|
||||||
typeDefs,
|
typeDefs,
|
||||||
resolvers,
|
resolvers,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.writeGenerateSchema(graphql.printSchema(schema));
|
writeGenerateSchema(graphql.printSchema(schema));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove custom scalar (like Upload);
|
||||||
|
typeDefs = Types.removeCustomScalar(typeDefs, resolvers);
|
||||||
|
|
||||||
|
return {
|
||||||
|
typeDefs: gql(typeDefs),
|
||||||
|
resolvers,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save into a file the readable GraphQL schema.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
|
||||||
|
const writeGenerateSchema = schema => {
|
||||||
|
return strapi.fs.writeAppFile('exports/graphql/schema.graphql', schema);
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildShadowCRUD = () => {
|
||||||
|
const modelSchema = Resolvers.buildShadowCRUD(
|
||||||
|
_.omitBy(strapi.models, model => model.internal === true)
|
||||||
|
);
|
||||||
|
|
||||||
|
const pluginSchemas = Object.keys(strapi.plugins).reduce((acc, plugin) => {
|
||||||
|
const schemas = Resolvers.buildShadowCRUD(strapi.plugins[plugin].models);
|
||||||
|
return acc.concat(schemas);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const componentSchemas = Object.values(strapi.components).map(compo =>
|
||||||
|
Resolvers.buildComponent(compo)
|
||||||
|
);
|
||||||
|
|
||||||
|
const schema = { definition: '', resolvers: {}, query: {}, mutation: {} };
|
||||||
|
mergeSchemas(schema, modelSchema, ...pluginSchemas, ...componentSchemas);
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildResolvers = resolvers => {
|
||||||
|
// Transform object to only contain function.
|
||||||
|
Object.keys(resolvers).reduce((acc, type) => {
|
||||||
|
if (graphql.isScalarType(acc[type])) {
|
||||||
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove custom scalar (like Upload);
|
return Object.keys(acc[type]).reduce((acc, resolverName) => {
|
||||||
typeDefs = Types.removeCustomScalar(typeDefs, resolvers);
|
const resolverObj = acc[type][resolverName];
|
||||||
|
|
||||||
return {
|
// Disabled this query.
|
||||||
typeDefs: gql(typeDefs),
|
if (resolverObj === false) {
|
||||||
resolvers,
|
delete acc[type][resolverName];
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save into a file the readable GraphQL schema.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
writeGenerateSchema: schema => {
|
|
||||||
return strapi.fs.writeAppFile('exports/graphql/schema.graphql', schema);
|
|
||||||
},
|
|
||||||
|
|
||||||
buildShadowCRUD() {
|
|
||||||
const modelSchema = Resolvers.buildShadowCRUD(
|
|
||||||
_.omitBy(strapi.models, model => model.internal === true)
|
|
||||||
);
|
|
||||||
|
|
||||||
const pluginSchemas = Object.keys(strapi.plugins).reduce((acc, plugin) => {
|
|
||||||
const schemas = Resolvers.buildShadowCRUD(strapi.plugins[plugin].models);
|
|
||||||
return acc.concat(schemas);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const componentSchemas = Object.values(strapi.components).map(compo =>
|
|
||||||
Resolvers.buildComponent(compo)
|
|
||||||
);
|
|
||||||
|
|
||||||
const schema = { definition: '', resolvers: {}, query: {}, mutation: {} };
|
|
||||||
mergeSchemas(schema, modelSchema, ...pluginSchemas, ...componentSchemas);
|
|
||||||
|
|
||||||
return schema;
|
|
||||||
},
|
|
||||||
|
|
||||||
buildResolvers(resolvers) {
|
|
||||||
// Transform object to only contain function.
|
|
||||||
Object.keys(resolvers).reduce((acc, type) => {
|
|
||||||
if (graphql.isScalarType(acc[type])) {
|
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(acc[type]).reduce((acc, resolverName) => {
|
if (_.isFunction(resolverObj)) {
|
||||||
const resolverObj = acc[type][resolverName];
|
|
||||||
|
|
||||||
// Disabled this query.
|
|
||||||
if (resolverObj === false) {
|
|
||||||
delete acc[type][resolverName];
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_.isFunction(resolverObj)) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'Mutation': {
|
|
||||||
acc[type][resolverName] = buildMutation(resolverName, resolverObj);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'Query':
|
|
||||||
default: {
|
|
||||||
acc[type][resolverName] = buildQuery(resolverName, resolverObj);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, acc);
|
}
|
||||||
}, resolvers);
|
|
||||||
},
|
switch (type) {
|
||||||
|
case 'Mutation': {
|
||||||
|
acc[type][resolverName] = buildMutation(resolverName, resolverObj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'Query':
|
||||||
|
default: {
|
||||||
|
acc[type][resolverName] = buildQuery(resolverName, resolverObj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, acc);
|
||||||
|
}, resolvers);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
@ -501,19 +498,23 @@ const getActionDetails = resolver => {
|
|||||||
*/
|
*/
|
||||||
const isResolvablePath = path => _.isString(path) && !_.isEmpty(path);
|
const isResolvablePath = path => _.isString(path) && !_.isEmpty(path);
|
||||||
|
|
||||||
const getPolicies = (config, { plugin, api } = {}) => {
|
const getPolicies = config => {
|
||||||
const { resolver, policies = [], resolverOf } = config;
|
const { resolver, policies = [], resolverOf } = config;
|
||||||
|
|
||||||
|
const { api, plugin } = config['_metadatas'] || {};
|
||||||
|
|
||||||
const policyFns = [];
|
const policyFns = [];
|
||||||
|
|
||||||
const { controller, action } = isResolvablePath(resolverOf)
|
const { controller, action, plugin: pathPlugin } = isResolvablePath(
|
||||||
|
resolverOf
|
||||||
|
)
|
||||||
? getActionDetails(resolverOf)
|
? getActionDetails(resolverOf)
|
||||||
: getActionDetails(resolver);
|
: getActionDetails(resolver);
|
||||||
|
|
||||||
const globalPolicy = policyUtils.globalPolicy({
|
const globalPolicy = policyUtils.globalPolicy({
|
||||||
controller,
|
controller,
|
||||||
action,
|
action,
|
||||||
plugin,
|
plugin: pathPlugin,
|
||||||
});
|
});
|
||||||
|
|
||||||
policyFns.push(globalPolicy);
|
policyFns.push(globalPolicy);
|
||||||
@ -530,4 +531,9 @@ const getPolicies = (config, { plugin, api } = {}) => {
|
|||||||
return policyFns;
|
return policyFns;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = schemaBuilder;
|
module.exports = {
|
||||||
|
generateSchema,
|
||||||
|
getDescription,
|
||||||
|
formatGQL,
|
||||||
|
buildQuery,
|
||||||
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user