164 lines
4.2 KiB
JavaScript
Raw Normal View History

'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('./type-builder');
const { buildModels } = require('./type-definitions');
const { mergeSchemas, createDefaultSchema, diffResolvers } = require('./utils');
const { toSDL } = require('./schema-definitions');
const { buildQuery, buildMutation } = require('./resolvers-builder');
/**
* Generate GraphQL schema.
*
* @return Schema
*/
const generateSchema = () => {
const shadowCRUDEnabled = strapi.plugins.graphql.config.shadowCRUD !== false;
// Generate type definition and query/mutation for models.
const shadowCRUD = shadowCRUDEnabled ? buildModelsShadowCRUD() : createDefaultSchema();
const _schema = strapi.plugins.graphql.config._schema.graphql;
// Extract custom definition, query or resolver.
const { definition, query, mutation, resolver = {} } = _schema;
// Polymorphic.
const polymorphicSchema = Types.addPolymorphicUnionType(definition + shadowCRUD.definition);
const builtResolvers = _.merge({}, shadowCRUD.resolvers, polymorphicSchema.resolvers);
const extraResolvers = diffResolvers(_schema.resolver, builtResolvers);
const resolvers = _.merge({}, builtResolvers, buildResolvers(extraResolvers));
// Return empty schema when there is no model.
if (_.isEmpty(shadowCRUD.definition) && _.isEmpty(definition)) {
return {};
}
const queryFields = shadowCRUD.query && toSDL(shadowCRUD.query, resolver.Query, null, 'query');
const mutationFields =
shadowCRUD.mutation && toSDL(shadowCRUD.mutation, resolver.Mutation, null, 'mutation');
const scalars = Types.getScalars();
Object.assign(resolvers, scalars);
const scalarDef = Object.keys(scalars)
.map(key => `scalar ${key}`)
.join('\n');
// Concatenate.
let typeDefs = `
${definition}
${shadowCRUD.definition}
${polymorphicSchema.definition}
${Types.addInput()}
type Query {
${queryFields}
${query}
}
type Mutation {
${mutationFields}
${mutation}
}
${scalarDef}
`;
// // Build schema.
if (strapi.config.environment !== 'production') {
// Write schema.
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
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 buildModelsShadowCRUD = () => {
const models = Object.values(strapi.models).filter(model => model.internal !== true);
const pluginModels = Object.values(strapi.plugins)
.map(plugin => Object.values(plugin.models) || [])
.reduce((acc, arr) => acc.concat(arr), []);
const components = Object.values(strapi.components);
return mergeSchemas(
createDefaultSchema(),
...buildModels([...models, ...pluginModels, ...components])
);
};
const buildResolvers = resolvers => {
// Transform object to only contain function.
return Object.keys(resolvers).reduce((acc, type) => {
if (graphql.isScalarType(resolvers[type])) {
return acc;
}
return Object.keys(resolvers[type]).reduce((acc, resolverName) => {
const resolverObj = resolvers[type][resolverName];
// Disabled this query.
if (resolverObj === false) return acc;
if (_.isFunction(resolverObj)) {
return _.set(acc, [type, resolverName], resolverObj);
}
switch (type) {
case 'Mutation': {
_.set(acc, [type, resolverName], buildMutation(resolverName, resolverObj));
break;
}
default: {
_.set(acc, [type, resolverName], buildQuery(resolverName, resolverObj));
break;
}
}
return acc;
}, acc);
}, {});
};
module.exports = {
generateSchema,
};