mirror of
https://github.com/strapi/strapi.git
synced 2025-07-26 02:20:32 +00:00
189 lines
6.6 KiB
JavaScript
189 lines
6.6 KiB
JavaScript
'use strict';
|
|
|
|
const { mergeSchemas, addResolversToSchema } = require('@graphql-tools/schema');
|
|
const { pruneSchema } = require('@graphql-tools/utils');
|
|
const { makeSchema } = require('nexus');
|
|
const { prop, startsWith } = require('lodash/fp');
|
|
|
|
const { wrapResolvers } = require('./wrap-resolvers');
|
|
const {
|
|
registerSingleType,
|
|
registerCollectionType,
|
|
registerComponent,
|
|
registerScalars,
|
|
registerInternals,
|
|
registerPolymorphicContentType,
|
|
contentType: {
|
|
registerEnumsDefinition,
|
|
registerInputsDefinition,
|
|
registerFiltersDefinition,
|
|
registerDynamicZonesDefinition,
|
|
},
|
|
} = require('./register-functions');
|
|
|
|
module.exports = ({ strapi }) => {
|
|
const { service: getGraphQLService } = strapi.plugin('graphql');
|
|
const { config } = strapi.plugin('graphql');
|
|
|
|
const { KINDS, GENERIC_MORPH_TYPENAME } = getGraphQLService('constants');
|
|
const extensionService = getGraphQLService('extension');
|
|
|
|
// Type Registry
|
|
let registry;
|
|
// Builders Instances
|
|
let builders;
|
|
|
|
const buildSchema = () => {
|
|
const isShadowCRUDEnabled = !!config('shadowCRUD');
|
|
|
|
// Create a new empty type registry
|
|
registry = getGraphQLService('type-registry').new();
|
|
|
|
// Reset the builders instances associated to the
|
|
// content-api, and link the new type registry
|
|
builders = getGraphQLService('builders').new('content-api', registry);
|
|
|
|
registerScalars({ registry, strapi });
|
|
registerInternals({ registry, strapi });
|
|
|
|
if (isShadowCRUDEnabled) {
|
|
shadowCRUD();
|
|
}
|
|
|
|
// Build a merged schema from both Nexus types & SDL type definitions
|
|
const schema = buildMergedSchema({ registry });
|
|
|
|
// Generate the extension configuration for the content API.
|
|
// This extension instance needs to be generated after the Nexus schema's
|
|
// generation, so that configurations created during types definitions
|
|
// can be registered before being used in the wrap resolvers operation
|
|
const extension = extensionService.generate({ typeRegistry: registry });
|
|
|
|
// Add the extension's resolvers to the final schema
|
|
const schemaWithResolvers = addResolversToSchema(schema, extension.resolvers);
|
|
|
|
// Create a configuration object for the artifacts generation
|
|
const outputs = {
|
|
schema: config('artifacts.schema', false),
|
|
typegen: config('artifacts.typegen', false),
|
|
};
|
|
|
|
const currentEnv = strapi.config.get('environment');
|
|
|
|
const nexusSchema = makeSchema({
|
|
// Build the schema from the merged GraphQL schema.
|
|
// Since we're passing the schema to the mergeSchema property, it'll transform our SDL type definitions
|
|
// into Nexus type definition, thus allowing them to be handled by Nexus plugins & other processing
|
|
mergeSchema: { schema: schemaWithResolvers },
|
|
|
|
// Apply user-defined plugins
|
|
plugins: extension.plugins,
|
|
|
|
// Whether to generate artifacts (GraphQL schema, TS types definitions) or not.
|
|
// By default, we generate artifacts only on development environment
|
|
shouldGenerateArtifacts: config('generateArtifacts', currentEnv === 'development'),
|
|
|
|
// Artifacts generation configuration
|
|
outputs,
|
|
});
|
|
|
|
// Wrap resolvers if needed (auth, middlewares, policies...) as configured in the extension
|
|
const wrappedNexusSchema = wrapResolvers({ schema: nexusSchema, strapi, extension });
|
|
|
|
// Prune schema, remove unused types
|
|
// eg: removes registered subscriptions if they're disabled in the config)
|
|
const prunedNexusSchema = pruneSchema(wrappedNexusSchema);
|
|
|
|
return prunedNexusSchema;
|
|
};
|
|
|
|
const buildMergedSchema = ({ registry }) => {
|
|
// Here we extract types, plugins & typeDefs from a temporary generated
|
|
// extension since there won't be any addition allowed after schemas generation
|
|
const { types, typeDefs = [] } = extensionService.generate({ typeRegistry: registry });
|
|
|
|
// Nexus schema built with user-defined & shadow CRUD auto generated Nexus types
|
|
const nexusSchema = makeSchema({ types: [registry.definitions, types] });
|
|
|
|
// Merge type definitions with the Nexus schema
|
|
return mergeSchemas({
|
|
typeDefs,
|
|
// Give access to the shadowCRUD & nexus based types
|
|
// Note: This is necessary so that types defined in SDL can reference types defined with Nexus
|
|
schemas: [nexusSchema],
|
|
});
|
|
};
|
|
|
|
const shadowCRUD = () => {
|
|
const extensionService = getGraphQLService('extension');
|
|
|
|
// Get every content type & component defined in Strapi
|
|
const contentTypes = [
|
|
...Object.values(strapi.components),
|
|
...Object.values(strapi.contentTypes),
|
|
];
|
|
|
|
// 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()
|
|
);
|
|
|
|
// Generate and register definitions for every content type
|
|
registerAPITypes(contentTypesWithShadowCRUD);
|
|
|
|
// Generate and register polymorphic types' definitions
|
|
registerMorphTypes(contentTypesWithShadowCRUD);
|
|
};
|
|
|
|
/**
|
|
* 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 };
|
|
};
|