mirror of
https://github.com/strapi/strapi.git
synced 2025-07-16 13:32:05 +00:00
179 lines
4.8 KiB
JavaScript
179 lines
4.8 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* GraphQL.js service
|
|
*
|
|
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
|
*/
|
|
const { filterSchema } = require('@graphql-tools/utils');
|
|
const { buildFederatedSchema } = require('@apollo/federation');
|
|
const { gql, makeExecutableSchema } = require('apollo-server-koa');
|
|
const _ = require('lodash');
|
|
const graphql = require('graphql');
|
|
const PublicationState = require('../types/publication-state');
|
|
const Types = require('./type-builder');
|
|
const buildShadowCrud = require('./shadow-crud');
|
|
const { createDefaultSchema, diffResolvers } = require('./utils');
|
|
const { toSDL } = require('./schema-definitions');
|
|
const { buildQuery, buildMutation } = require('./resolvers-builder');
|
|
|
|
/**
|
|
* Generate GraphQL schema.
|
|
*
|
|
* @return Schema
|
|
*/
|
|
|
|
const generateSchema = () => {
|
|
const isFederated = _.get(strapi.plugins.graphql.config, 'federation', false);
|
|
const shadowCRUDEnabled = strapi.plugins.graphql.config.shadowCRUD !== false;
|
|
|
|
const _schema = strapi.plugins.graphql.config._schema.graphql;
|
|
|
|
const ctx = {
|
|
schema: _schema,
|
|
};
|
|
|
|
// Generate type definition and query/mutation for models.
|
|
const shadowCRUD = shadowCRUDEnabled ? buildShadowCrud(ctx) : createDefaultSchema();
|
|
|
|
// 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');
|
|
|
|
Object.assign(resolvers, PublicationState.resolver);
|
|
|
|
const scalars = Types.getScalars();
|
|
|
|
Object.assign(resolvers, scalars);
|
|
|
|
const scalarDef = Object.keys(scalars)
|
|
.map(key => `scalar ${key}`)
|
|
.join('\n');
|
|
|
|
// Concatenate.
|
|
// Manually defined to avoid exposing all attributes (like password etc.)
|
|
let typeDefs = `
|
|
${definition}
|
|
${shadowCRUD.definition}
|
|
${polymorphicSchema.definition}
|
|
${Types.addInput()}
|
|
|
|
${PublicationState.definition}
|
|
|
|
type AdminUser {
|
|
id: ID!
|
|
username: String
|
|
firstname: String!
|
|
lastname: String!
|
|
}
|
|
|
|
type Query {
|
|
${queryFields}
|
|
${query}
|
|
}
|
|
|
|
type Mutation {
|
|
${mutationFields}
|
|
${mutation}
|
|
}
|
|
${scalarDef}
|
|
`;
|
|
|
|
// Build schema.
|
|
const schema = makeExecutableSchema({
|
|
typeDefs,
|
|
resolvers,
|
|
});
|
|
|
|
const generatedSchema = filterDisabledResolvers(schema, extraResolvers);
|
|
|
|
if (strapi.config.environment !== 'production') {
|
|
writeGenerateSchema(generatedSchema);
|
|
}
|
|
|
|
return isFederated ? getFederatedSchema(generatedSchema, resolvers) : generatedSchema;
|
|
};
|
|
|
|
const getFederatedSchema = (schema, resolvers) =>
|
|
buildFederatedSchema([{ typeDefs: gql(graphql.printSchema(schema)), resolvers }]);
|
|
|
|
const filterDisabledResolvers = (schema, extraResolvers) =>
|
|
filterSchema({
|
|
schema,
|
|
rootFieldFilter: (operationName, fieldName) => {
|
|
const resolver = _.get(extraResolvers[operationName], fieldName, true);
|
|
|
|
// resolvers set to false are filtered from the schema
|
|
if (resolver === false) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Save into a file the readable GraphQL schema.
|
|
*
|
|
* @return void
|
|
*/
|
|
const writeGenerateSchema = schema => {
|
|
const printSchema = graphql.printSchema(schema);
|
|
return strapi.fs.writeAppFile('exports/graphql/schema.graphql', printSchema);
|
|
};
|
|
|
|
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,
|
|
};
|