2018-03-27 17:15:28 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module dependencies
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Public node modules.
|
2018-03-29 14:03:09 +02:00
|
|
|
const _ = require('lodash');
|
2018-09-10 16:05:00 +08:00
|
|
|
const { ApolloServer } = require('apollo-server-koa');
|
2020-07-16 03:09:59 -04:00
|
|
|
const { buildFederatedSchema } = require('@apollo/federation');
|
2018-05-25 16:08:51 +02:00
|
|
|
const depthLimit = require('graphql-depth-limit');
|
2019-04-09 21:51:28 +02:00
|
|
|
const loadConfigs = require('./load-config');
|
2018-03-27 17:15:28 +02:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2018-03-27 17:15:28 +02:00
|
|
|
module.exports = strapi => {
|
2019-04-09 21:51:28 +02:00
|
|
|
const { appPath, installedPlugins } = strapi.config;
|
|
|
|
|
2018-03-27 17:15:28 +02:00
|
|
|
return {
|
2019-11-04 11:29:19 +01:00
|
|
|
async beforeInitialize() {
|
2018-03-28 20:13:09 +02:00
|
|
|
// Try to inject this hook just after the others hooks to skip the router processing.
|
2020-04-09 16:27:29 +02:00
|
|
|
if (!strapi.config.get('hook.load.after')) {
|
2018-04-02 18:33:12 +02:00
|
|
|
_.set(strapi.config.hook.load, 'after', []);
|
|
|
|
}
|
|
|
|
|
|
|
|
strapi.config.hook.load.after.push('graphql');
|
2018-03-30 17:05:24 +02:00
|
|
|
// Load core utils.
|
2019-04-09 21:51:28 +02:00
|
|
|
|
2019-06-10 20:37:13 +02:00
|
|
|
const { api, plugins, extensions } = await loadConfigs({
|
|
|
|
appPath,
|
|
|
|
installedPlugins,
|
|
|
|
});
|
|
|
|
_.merge(strapi, { api, plugins });
|
2018-03-31 18:55:08 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a merge of all the GraphQL configuration.
|
|
|
|
*/
|
2020-01-30 10:49:42 +01:00
|
|
|
const apisSchemas = Object.keys(strapi.api || {}).map(key => {
|
|
|
|
const schema = _.get(strapi.api[key], 'config.schema.graphql', {});
|
|
|
|
return attachMetadataToResolvers(schema, { api: key });
|
|
|
|
});
|
2019-04-09 21:51:28 +02:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const pluginsSchemas = Object.keys(strapi.plugins || {}).map(key => {
|
|
|
|
const schema = _.get(strapi.plugins[key], 'config.schema.graphql', {});
|
|
|
|
return attachMetadataToResolvers(schema, { plugin: key });
|
|
|
|
});
|
2019-04-09 21:51:28 +02:00
|
|
|
|
2020-01-30 10:49:42 +01:00
|
|
|
const extensionsSchemas = Object.keys(extensions || {}).map(key => {
|
|
|
|
const schema = _.get(extensions[key], 'config.schema.graphql', {});
|
|
|
|
return attachMetadataToResolvers(schema, { plugin: key });
|
|
|
|
});
|
2019-04-09 21:51:28 +02:00
|
|
|
|
2020-03-02 15:18:08 +01:00
|
|
|
const baseSchema = mergeSchemas([...apisSchemas, ...pluginsSchemas, ...extensionsSchemas]);
|
2019-06-10 20:37:13 +02:00
|
|
|
|
2019-04-09 21:51:28 +02:00
|
|
|
// save the final schema in the plugin's config
|
2020-03-02 15:18:08 +01:00
|
|
|
_.set(strapi, ['plugins', 'graphql', 'config', '_schema', 'graphql'], baseSchema);
|
2018-03-27 17:15:28 +02:00
|
|
|
},
|
|
|
|
|
2019-11-04 11:29:19 +01:00
|
|
|
initialize() {
|
2020-02-10 19:13:01 +01:00
|
|
|
const { typeDefs, resolvers } = strapi.plugins.graphql.services[
|
|
|
|
'schema-generator'
|
|
|
|
].generateSchema();
|
2018-03-27 17:15:28 +02:00
|
|
|
|
2018-09-10 16:05:00 +08:00
|
|
|
if (_.isEmpty(typeDefs)) {
|
2020-03-02 15:18:08 +01:00
|
|
|
strapi.log.warn('The GraphQL schema has not been generated because it is empty');
|
2018-03-29 14:03:09 +02:00
|
|
|
|
2019-08-14 14:15:45 +02:00
|
|
|
return;
|
2018-03-29 14:03:09 +02:00
|
|
|
}
|
|
|
|
|
2020-07-16 03:09:59 -04:00
|
|
|
// Get federation config
|
|
|
|
const isFederated = _.get(strapi.plugins.graphql, 'config.federation', false);
|
|
|
|
const schemaDef = {};
|
|
|
|
if (isFederated) {
|
|
|
|
schemaDef.schema = buildFederatedSchema([{ typeDefs, resolvers }]);
|
|
|
|
} else {
|
|
|
|
schemaDef.typeDefs = typeDefs;
|
|
|
|
schemaDef.resolvers = resolvers;
|
|
|
|
}
|
|
|
|
|
2018-09-10 16:05:00 +08:00
|
|
|
const serverParams = {
|
2020-07-16 03:09:59 -04:00
|
|
|
...schemaDef,
|
2019-03-25 16:37:46 +01:00
|
|
|
context: ({ ctx }) => {
|
|
|
|
// Initiliase loaders for this request.
|
2019-04-09 21:51:28 +02:00
|
|
|
// TODO: set loaders in the context not globally
|
2020-02-10 19:13:01 +01:00
|
|
|
|
|
|
|
strapi.plugins.graphql.services['data-loaders'].initializeLoader();
|
2019-03-25 16:37:46 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
context: ctx,
|
|
|
|
};
|
|
|
|
},
|
2020-06-16 20:57:05 +09:00
|
|
|
formatError: err => {
|
|
|
|
const formatError = _.get(strapi.plugins.graphql, 'config.formatError', null);
|
|
|
|
|
|
|
|
return typeof formatError === 'function' ? formatError(err) : err;
|
|
|
|
},
|
2018-09-10 16:05:00 +08:00
|
|
|
validationRules: [depthLimit(strapi.plugins.graphql.config.depthLimit)],
|
2018-12-14 14:26:40 +01:00
|
|
|
tracing: _.get(strapi.plugins.graphql, 'config.tracing', false),
|
2018-09-10 16:05:00 +08:00
|
|
|
playground: false,
|
2019-08-21 11:05:33 +02:00
|
|
|
cors: false,
|
|
|
|
bodyParserConfig: true,
|
2020-03-02 15:18:08 +01:00
|
|
|
introspection: _.get(strapi.plugins.graphql, 'config.introspection', true),
|
2020-07-29 20:01:32 +09:00
|
|
|
engine: _.get(strapi.plugins.graphql, 'config.engine', false),
|
2018-09-10 16:05:00 +08:00
|
|
|
};
|
2018-03-27 17:15:28 +02:00
|
|
|
|
2018-05-15 16:03:22 +02:00
|
|
|
// Disable GraphQL Playground in production environment.
|
2018-09-10 16:05:00 +08:00
|
|
|
if (
|
|
|
|
strapi.config.environment !== 'production' ||
|
|
|
|
strapi.plugins.graphql.config.playgroundAlways
|
|
|
|
) {
|
|
|
|
serverParams.playground = {
|
2020-05-08 13:50:00 +02:00
|
|
|
endpoint: `${strapi.config.server.url}${strapi.plugins.graphql.config.endpoint}`,
|
2019-11-04 11:29:19 +01:00
|
|
|
shareEnabled: strapi.plugins.graphql.config.shareEnabled,
|
2018-09-10 16:05:00 +08:00
|
|
|
};
|
2018-04-02 16:31:27 +02:00
|
|
|
}
|
2018-03-27 17:15:28 +02:00
|
|
|
|
2018-09-10 16:05:00 +08:00
|
|
|
const server = new ApolloServer(serverParams);
|
|
|
|
|
|
|
|
server.applyMiddleware({
|
|
|
|
app: strapi.app,
|
|
|
|
path: strapi.plugins.graphql.config.endpoint,
|
|
|
|
});
|
|
|
|
},
|
2018-03-27 17:15:28 +02:00
|
|
|
};
|
|
|
|
};
|
2019-04-09 21:51:28 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Merges a list of schemas
|
|
|
|
* @param {Array<Object>} schemas - The list of schemas to merge
|
|
|
|
*/
|
|
|
|
const mergeSchemas = schemas => {
|
|
|
|
return schemas.reduce((acc, el) => {
|
|
|
|
const { definition, query, mutation, type, resolver } = el;
|
|
|
|
|
|
|
|
return _.merge(acc, {
|
|
|
|
definition: `${acc.definition || ''} ${definition || ''}`,
|
|
|
|
query: `${acc.query || ''} ${query || ''}`,
|
|
|
|
mutation: `${acc.mutation || ''} ${mutation || ''}`,
|
|
|
|
type,
|
|
|
|
resolver,
|
|
|
|
});
|
|
|
|
}, {});
|
|
|
|
};
|