2021-09-01 12:06:51 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const {
|
|
|
|
mergeSchemas,
|
|
|
|
makeExecutableSchema,
|
|
|
|
addResolversToSchema,
|
|
|
|
} = require('@graphql-tools/schema');
|
|
|
|
const { makeSchema } = require('nexus');
|
2021-09-15 15:44:42 +02:00
|
|
|
const { pipe, prop, startsWith } = require('lodash/fp');
|
2021-09-01 12:06:51 +02:00
|
|
|
|
|
|
|
const { wrapResolvers } = require('./wrap-resolvers');
|
|
|
|
const {
|
|
|
|
registerSingleType,
|
|
|
|
registerCollectionType,
|
|
|
|
registerComponent,
|
|
|
|
registerScalars,
|
|
|
|
registerInternals,
|
|
|
|
registerPolymorphicContentType,
|
|
|
|
contentType: {
|
|
|
|
registerEnumsDefinition,
|
|
|
|
registerInputsDefinition,
|
|
|
|
registerFiltersDefinition,
|
|
|
|
registerDynamicZonesDefinition,
|
|
|
|
},
|
|
|
|
} = require('./register-functions');
|
|
|
|
|
|
|
|
module.exports = ({ strapi }) => {
|
2021-09-15 15:44:42 +02:00
|
|
|
const { service: getGraphQLService } = strapi.plugin('graphql');
|
|
|
|
|
|
|
|
const { KINDS, GENERIC_MORPH_TYPENAME } = getGraphQLService('constants');
|
|
|
|
|
2021-09-01 12:06:51 +02:00
|
|
|
// Type Registry
|
|
|
|
let registry;
|
|
|
|
// Builders Instances
|
|
|
|
let builders;
|
|
|
|
|
|
|
|
const buildSchema = () => {
|
2021-09-15 15:44:42 +02:00
|
|
|
const extensionService = getGraphQLService('extension');
|
|
|
|
|
2021-09-01 12:06:51 +02:00
|
|
|
// Create a new empty type registry
|
2021-09-15 15:44:42 +02:00
|
|
|
registry = getGraphQLService('type-registry').new();
|
2021-09-01 12:06:51 +02:00
|
|
|
|
|
|
|
// Reset the builders instances associated to the
|
|
|
|
// content-api, and link the new type registry
|
2021-09-15 15:44:42 +02:00
|
|
|
builders = getGraphQLService('builders').new('content-api', registry);
|
2021-09-01 12:06:51 +02:00
|
|
|
|
2021-09-15 15:44:42 +02:00
|
|
|
// Get every content type & component defined in Strapi
|
2021-09-01 12:06:51 +02:00
|
|
|
const contentTypes = [
|
|
|
|
...Object.values(strapi.components),
|
|
|
|
...Object.values(strapi.contentTypes),
|
|
|
|
];
|
|
|
|
|
2021-09-15 15:44:42 +02:00
|
|
|
// Disable Shadow CRUD for admin content types
|
|
|
|
contentTypes
|
|
|
|
.map(prop('uid'))
|
|
|
|
.filter(startsWith('admin::'))
|
|
|
|
.forEach(uid => extensionService.shadowCRUD(uid).disable());
|
|
|
|
|
|
|
|
const contentTypesWithShadowCRUD = contentTypes.filter(ct =>
|
|
|
|
extensionService.shadowCRUD(ct.uid).isEnabled()
|
|
|
|
);
|
|
|
|
|
2021-09-01 12:06:51 +02:00
|
|
|
registerScalars({ registry, strapi });
|
|
|
|
registerInternals({ registry, strapi });
|
|
|
|
|
|
|
|
// Generate and register definitions for every content type
|
2021-09-15 15:44:42 +02:00
|
|
|
registerAPITypes(contentTypesWithShadowCRUD);
|
2021-09-01 12:06:51 +02:00
|
|
|
|
|
|
|
// Generate and register polymorphic types' definitions
|
2021-09-15 15:44:42 +02:00
|
|
|
registerMorphTypes(contentTypesWithShadowCRUD);
|
2021-09-01 12:06:51 +02:00
|
|
|
|
|
|
|
// Generate the extension configuration for the content API
|
2021-09-15 15:44:42 +02:00
|
|
|
const extension = extensionService.generate({ typeRegistry: registry });
|
2021-09-01 12:06:51 +02:00
|
|
|
|
|
|
|
return pipe(
|
|
|
|
// Build a collection of schema based on the
|
|
|
|
// type registry & the extension configuration
|
|
|
|
buildSchemas,
|
|
|
|
// Merge every created schema into a single one
|
|
|
|
schemas => mergeSchemas({ schemas }),
|
|
|
|
// Add the extension's resolvers to the final schema
|
|
|
|
schema => addResolversToSchema(schema, extension.resolvers),
|
|
|
|
// Wrap resolvers if needed (auth, middlewares, policies...) as configured in the extension
|
2021-09-07 11:23:49 +02:00
|
|
|
schema => wrapResolvers({ schema, strapi, extension })
|
2021-09-01 12:06:51 +02:00
|
|
|
)({ registry, extension });
|
|
|
|
};
|
|
|
|
|
|
|
|
const buildSchemas = ({ registry, extension }) => {
|
|
|
|
const { types, plugins, typeDefs = [] } = extension;
|
|
|
|
|
|
|
|
// Create a new Nexus schema (shadow CRUD) & add it to the schemas collection
|
|
|
|
const nexusSchema = makeSchema({
|
|
|
|
types: [
|
|
|
|
// Add the auto-generated Nexus types (shadow CRUD)
|
|
|
|
registry.definitions,
|
|
|
|
// Add every Nexus type registered using the extension service
|
|
|
|
types,
|
|
|
|
],
|
|
|
|
|
|
|
|
plugins: [
|
|
|
|
// Add every plugin registered using the extension service
|
|
|
|
...plugins,
|
|
|
|
],
|
|
|
|
});
|
|
|
|
|
|
|
|
// Build schemas based on SDL type definitions (defined in the extension)
|
|
|
|
const sdlSchemas = typeDefs.map(sdl => makeExecutableSchema({ typeDefs: sdl }));
|
|
|
|
|
|
|
|
return [nexusSchema, ...sdlSchemas];
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register needed GraphQL types for every content type
|
|
|
|
* @param {object[]} contentTypes
|
|
|
|
*/
|
|
|
|
const registerAPITypes = contentTypes => {
|
|
|
|
for (const contentType of contentTypes) {
|
|
|
|
const { kind, modelType } = contentType;
|
|
|
|
|
|
|
|
const registerOptions = { registry, strapi, builders };
|
|
|
|
|
|
|
|
// Generate various types associated to the content type
|
|
|
|
// (enums, dynamic-zones, filters, inputs...)
|
|
|
|
registerEnumsDefinition(contentType, registerOptions);
|
|
|
|
registerDynamicZonesDefinition(contentType, registerOptions);
|
|
|
|
registerFiltersDefinition(contentType, registerOptions);
|
|
|
|
registerInputsDefinition(contentType, registerOptions);
|
|
|
|
|
|
|
|
// Generate & register component's definition
|
|
|
|
if (modelType === 'component') {
|
|
|
|
registerComponent(contentType, registerOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate & register single type's definition
|
|
|
|
else if (kind === 'singleType') {
|
|
|
|
registerSingleType(contentType, registerOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate & register collection type's definition
|
|
|
|
else if (kind === 'collectionType') {
|
|
|
|
registerCollectionType(contentType, registerOptions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const registerMorphTypes = contentTypes => {
|
|
|
|
// Create & register a union type that includes every type or component registered
|
|
|
|
const genericMorphType = builders.buildGenericMorphDefinition();
|
|
|
|
registry.register(GENERIC_MORPH_TYPENAME, genericMorphType, { kind: KINDS.morph });
|
|
|
|
|
|
|
|
for (const contentType of contentTypes) {
|
|
|
|
registerPolymorphicContentType(contentType, { registry, strapi });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return { buildSchema };
|
|
|
|
};
|