mirror of
https://github.com/strapi/strapi.git
synced 2025-09-03 22:03:08 +00:00
305 lines
9.1 KiB
JavaScript
305 lines
9.1 KiB
JavaScript
'use strict';
|
|
|
|
const { prop } = require('lodash/fp');
|
|
const { makeSchema, unionType } = require('nexus');
|
|
|
|
const createBuilders = require('../builders');
|
|
const { utils, constants, scalars, buildInternals } = require('../../types');
|
|
|
|
const { create: createTypeRegistry } = require('../../type-registry');
|
|
|
|
module.exports = strapi => {
|
|
// todo[v4]: Get the service directly with something like strapi.plugin().service()
|
|
const registry = createTypeRegistry();
|
|
const builders = createBuilders({ strapi, registry });
|
|
|
|
const registerAPITypes = contentTypes => {
|
|
for (const contentType of contentTypes) {
|
|
const { kind, modelType } = contentType;
|
|
|
|
// Generate enums & dynamic zones
|
|
registerEnumsDefinition(contentType);
|
|
registerDynamicZonesDefinition(contentType);
|
|
registerFiltersDefinition(contentType);
|
|
registerInputsDefinition(contentType);
|
|
|
|
// Generate & register component's definition
|
|
if (modelType === 'component') {
|
|
registerComponent(contentType);
|
|
}
|
|
|
|
// Generate & register single type's definition
|
|
else if (kind === 'singleType') {
|
|
registerSingleType(contentType);
|
|
}
|
|
|
|
// Generate & register collection type's definition
|
|
else if (kind === 'collectionType') {
|
|
registerCollectionType(contentType);
|
|
}
|
|
}
|
|
};
|
|
|
|
const registerMorphTypes = contentTypes => {
|
|
// Create & register a union type that includes every type or component registered
|
|
const genericMorphType = builders.buildGenericMorphDefinition();
|
|
registry.register(constants.GENERIC_MORPH_TYPENAME, genericMorphType, { kind: 'morphs' });
|
|
|
|
// For every content type
|
|
contentTypes.forEach(contentType => {
|
|
const { attributes = {} } = contentType;
|
|
|
|
// Isolate its polymorphic attributes
|
|
const morphAttributes = Object.entries(attributes).filter(([, attribute]) =>
|
|
utils.isMorphRelation(attribute)
|
|
);
|
|
|
|
// For each one of those polymorphic attribute
|
|
for (const [attributeName, attribute] of morphAttributes) {
|
|
const name = utils.getMorphRelationTypeName(contentType, attributeName);
|
|
const { target } = attribute;
|
|
|
|
// Ignore those whose target is not an array
|
|
if (!Array.isArray(target)) {
|
|
continue;
|
|
}
|
|
|
|
// Transform target UIDs into types names
|
|
const members = target
|
|
// Get content types definitions
|
|
.map(uid => strapi.getModel(uid))
|
|
// Resolve types names
|
|
.map(contentType => utils.getTypeName(contentType));
|
|
|
|
// Register the new polymorphic union type
|
|
registry.register(
|
|
name,
|
|
|
|
unionType({
|
|
name,
|
|
|
|
resolveType: prop('__typename'),
|
|
|
|
definition(t) {
|
|
t.members(...members);
|
|
},
|
|
}),
|
|
|
|
{ kind: 'morphs', contentType, attributeName }
|
|
);
|
|
}
|
|
});
|
|
};
|
|
|
|
const registerComponent = contentType => {
|
|
const name = utils.getComponentName(contentType);
|
|
const definition = builders.buildTypeDefinition(contentType);
|
|
|
|
registry.register(name, definition, { kind: 'components', contentType });
|
|
};
|
|
|
|
const registerSingleType = contentType => {
|
|
const types = {
|
|
base: utils.getTypeName(contentType),
|
|
entity: utils.getEntityName(contentType),
|
|
response: utils.getEntityResponseName(contentType),
|
|
queries: utils.getEntityQueriesTypeName(contentType),
|
|
mutations: utils.getEntityMutationsTypeName(contentType),
|
|
};
|
|
|
|
const getConfig = kind => ({ kind, contentType });
|
|
|
|
// Single type's definition
|
|
registry.register(types.base, builders.buildTypeDefinition(contentType), getConfig('types'));
|
|
|
|
// Higher level entity definition
|
|
registry.register(
|
|
types.entity,
|
|
builders.buildEntityDefinition(contentType),
|
|
getConfig('entities')
|
|
);
|
|
|
|
// Responses definition
|
|
registry.register(
|
|
types.response,
|
|
builders.buildResponseDefinition(contentType),
|
|
getConfig('entitiesResponses')
|
|
);
|
|
|
|
// Queries
|
|
registry.register(
|
|
types.queries,
|
|
builders.buildSingleTypeQueries(contentType),
|
|
getConfig('queries')
|
|
);
|
|
|
|
registry.register(
|
|
types.mutations,
|
|
builders.buildSingleTypeMutations(contentType),
|
|
getConfig('mutations')
|
|
);
|
|
};
|
|
|
|
const registerCollectionType = contentType => {
|
|
// Types name (as string)
|
|
const types = {
|
|
base: utils.getTypeName(contentType),
|
|
entity: utils.getEntityName(contentType),
|
|
response: utils.getEntityResponseName(contentType),
|
|
responseCollection: utils.getEntityResponseCollectionName(contentType),
|
|
queries: utils.getEntityQueriesTypeName(contentType),
|
|
mutations: utils.getEntityMutationsTypeName(contentType),
|
|
};
|
|
|
|
const getConfig = kind => ({ kind, contentType });
|
|
|
|
// Type definition
|
|
registry.register(types.base, builders.buildTypeDefinition(contentType), getConfig('types'));
|
|
|
|
// Higher level entity definition
|
|
registry.register(
|
|
types.entity,
|
|
builders.buildEntityDefinition(contentType),
|
|
getConfig('entities')
|
|
);
|
|
|
|
// Responses definition
|
|
registry.register(
|
|
types.response,
|
|
builders.buildResponseDefinition(contentType),
|
|
getConfig('entitiesResponses')
|
|
);
|
|
|
|
registry.register(
|
|
types.responseCollection,
|
|
builders.buildResponseCollectionDefinition(contentType),
|
|
getConfig('entitiesResponsesCollection')
|
|
);
|
|
|
|
// Query extensions
|
|
registry.register(
|
|
types.queries,
|
|
builders.buildCollectionTypeQueries(contentType),
|
|
getConfig('queries')
|
|
);
|
|
|
|
// Mutation extensions
|
|
registry.register(
|
|
types.mutations,
|
|
builders.buildCollectionTypeMutations(contentType),
|
|
getConfig('mutations')
|
|
);
|
|
};
|
|
|
|
const registerEnumsDefinition = contentType => {
|
|
const { attributes } = contentType;
|
|
|
|
const enumAttributes = Object.keys(attributes).filter(attributeName =>
|
|
utils.isEnumeration(attributes[attributeName])
|
|
);
|
|
|
|
for (const attributeName of enumAttributes) {
|
|
const attribute = attributes[attributeName];
|
|
|
|
const enumName = utils.getEnumName(contentType, attributeName);
|
|
const enumDefinition = builders.buildEnumTypeDefinition(attribute, enumName);
|
|
|
|
registry.register(enumName, enumDefinition, {
|
|
kind: 'enums',
|
|
contentType,
|
|
attributeName,
|
|
attribute,
|
|
});
|
|
}
|
|
};
|
|
|
|
const registerDynamicZonesDefinition = contentType => {
|
|
const { attributes } = contentType;
|
|
|
|
const dynamicZoneAttributes = Object.keys(attributes).filter(attributeName =>
|
|
utils.isDynamicZone(attributes[attributeName])
|
|
);
|
|
|
|
for (const attributeName of dynamicZoneAttributes) {
|
|
const attribute = attributes[attributeName];
|
|
const dzName = utils.getDynamicZoneName(contentType, attributeName);
|
|
const dzInputName = utils.getDynamicZoneInputName(contentType, attributeName);
|
|
|
|
const [type, input] = builders.buildDynamicZoneDefinition(attribute, dzName, dzInputName);
|
|
|
|
if (type && input) {
|
|
const baseConfig = {
|
|
contentType,
|
|
attributeName,
|
|
attribute,
|
|
};
|
|
|
|
registry.register(dzName, type, { kind: 'dynamic-zones', ...baseConfig });
|
|
registry.register(dzInputName, input, { kind: 'inputs', ...baseConfig });
|
|
}
|
|
}
|
|
};
|
|
|
|
const registerFiltersDefinition = contentType => {
|
|
const type = utils.getFiltersInputTypeName(contentType);
|
|
const definition = builders.buildContentTypeFilters(contentType);
|
|
|
|
registry.register(type, definition, { kind: 'filters-inputs', contentType });
|
|
};
|
|
|
|
const registerInputsDefinition = contentType => {
|
|
const { modelType } = contentType;
|
|
|
|
const type = (modelType === 'component'
|
|
? utils.getComponentInputName
|
|
: utils.getContentTypeInputName
|
|
).call(null, contentType);
|
|
|
|
const definition = builders.buildInputType(contentType);
|
|
|
|
registry.register(type, definition, { kind: 'inputs', contentType });
|
|
};
|
|
|
|
return () => {
|
|
const contentTypes = [
|
|
...Object.values(strapi.components),
|
|
...Object.values(strapi.contentTypes),
|
|
];
|
|
|
|
// Register needed scalar types
|
|
Object.entries(scalars).forEach(([name, definition]) => {
|
|
registry.register(name, definition, { kind: 'scalar' });
|
|
});
|
|
|
|
// Register Strapi's internal types
|
|
const internals = buildInternals({ strapi });
|
|
|
|
for (const [kind, definitions] of Object.entries(internals)) {
|
|
registry.registerMany(Object.entries(definitions), { kind });
|
|
}
|
|
|
|
// Generate and register definitions for every content type
|
|
registerAPITypes(contentTypes);
|
|
|
|
// Generate and register polymorphic types' definitions
|
|
registerMorphTypes(contentTypes);
|
|
|
|
// Return a brand new Nexus schema
|
|
return makeSchema({
|
|
// Exhaustive list of the content-api's types definitions
|
|
types: registry.definitions,
|
|
|
|
// Plugins
|
|
plugins: [],
|
|
|
|
// Auto-gen tools configuration (.graphql, .ts)
|
|
// shouldGenerateArtifacts: process.env.NODE_ENV === 'development',
|
|
//
|
|
outputs: {
|
|
// typegen: join(__dirname, '..', 'nexus-typegen.ts'),
|
|
// schema: join(__dirname, '../../../../../..', 'schema.graphql'),
|
|
},
|
|
});
|
|
};
|
|
};
|