mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 19:04:38 +00:00
Use objects instead of strings to declare queries & mutattions
This commit is contained in:
parent
a408dc1940
commit
02342ace0a
@ -1,6 +1,6 @@
|
||||
module.exports = ({ env }) => ({
|
||||
graphql: {
|
||||
amountLimit: 5,
|
||||
amountLimit: 50,
|
||||
depthLimit: 10,
|
||||
apolloServer: {
|
||||
tracing: true,
|
||||
|
||||
@ -50,9 +50,7 @@ module.exports = strapi => {
|
||||
});
|
||||
_.merge(strapi, { api, plugins });
|
||||
|
||||
/*
|
||||
* Create a merge of all the GraphQL configuration.
|
||||
*/
|
||||
// Create a merge of all the GraphQL configuration.
|
||||
const apisSchemas = Object.keys(strapi.api || {}).map(key => {
|
||||
const schema = _.get(strapi.api[key], 'config.schema.graphql', {});
|
||||
return attachMetadataToResolvers(schema, { api: key });
|
||||
@ -68,10 +66,10 @@ module.exports = strapi => {
|
||||
return attachMetadataToResolvers(schema, { plugin: key });
|
||||
});
|
||||
|
||||
const baseSchema = mergeSchemas([...apisSchemas, ...pluginsSchemas, ...extensionsSchemas]);
|
||||
const baseSchema = mergeSchemas([...pluginsSchemas, ...extensionsSchemas, ...apisSchemas]);
|
||||
|
||||
// save the final schema in the plugin's config
|
||||
_.set(strapi, ['plugins', 'graphql', 'config', '_schema', 'graphql'], baseSchema);
|
||||
_.set(strapi.plugins.graphql, 'config._schema.graphql', baseSchema);
|
||||
},
|
||||
|
||||
initialize() {
|
||||
|
||||
@ -494,8 +494,9 @@ const formatConnectionAggregator = function(fields, model, modelName) {
|
||||
* }
|
||||
*
|
||||
*/
|
||||
const formatModelConnectionsGQL = function({ fields, model, name, resolver }) {
|
||||
const { globalId } = model;
|
||||
const formatModelConnectionsGQL = function({ fields, model: contentType, name, resolver }) {
|
||||
const { globalId } = contentType;
|
||||
const model = strapi.getModel(contentType.uid);
|
||||
|
||||
const connectionGlobalId = `${globalId}Connection`;
|
||||
|
||||
@ -514,8 +515,6 @@ const formatModelConnectionsGQL = function({ fields, model, name, resolver }) {
|
||||
}
|
||||
modelConnectionTypes += groupByFormat.type;
|
||||
|
||||
const queryName = `${pluralName}Connection(sort: String, limit: Int, start: Int, where: JSON)`;
|
||||
|
||||
const connectionResolver = buildQueryResolver(`${pluralName}Connection.values`, resolver);
|
||||
|
||||
const connectionQueryName = `${pluralName}Connection`;
|
||||
@ -524,7 +523,16 @@ const formatModelConnectionsGQL = function({ fields, model, name, resolver }) {
|
||||
globalId: connectionGlobalId,
|
||||
definition: modelConnectionTypes,
|
||||
query: {
|
||||
[queryName]: connectionGlobalId,
|
||||
[`${pluralName}Connection`]: {
|
||||
args: {
|
||||
sort: 'String',
|
||||
limit: 'Int',
|
||||
start: 'Int',
|
||||
where: 'JSON',
|
||||
...(resolver.args || {}),
|
||||
},
|
||||
type: connectionGlobalId,
|
||||
},
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
|
||||
8
packages/strapi-plugin-graphql/services/hooks.js
Normal file
8
packages/strapi-plugin-graphql/services/hooks.js
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const { createAsyncSeriesWaterfallHook } = require('strapi-utils').hooks;
|
||||
|
||||
module.exports = {
|
||||
createQuery: createAsyncSeriesWaterfallHook(),
|
||||
createMutation: createAsyncSeriesWaterfallHook(),
|
||||
};
|
||||
@ -75,6 +75,11 @@ const buildMutationContext = ({ options, graphqlContext }) => {
|
||||
ctx.request.body = options;
|
||||
}
|
||||
|
||||
// pass extra args as ctx.query
|
||||
if (options.input) {
|
||||
ctx.query = convertToParams(_.omit(options, 'input'));
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
|
||||
|
||||
@ -71,14 +71,38 @@ const fieldsToSDL = ({ fields, configurations, model }) => {
|
||||
const operationToSDL = ({ fields, configurations }) => {
|
||||
return Object.entries(fields)
|
||||
.map(([key, value]) => {
|
||||
const [attr] = key.split('(');
|
||||
const attributeName = _.trim(attr);
|
||||
if (typeof value === 'string') {
|
||||
const [attr] = key.split('(');
|
||||
const attributeName = _.trim(attr);
|
||||
|
||||
return applyMetadatas(`${key}: ${value}`, configurations[attributeName]);
|
||||
return applyMetadatas(`${key}: ${value}`, configurations[attributeName]);
|
||||
} else {
|
||||
const { args = {}, type } = value;
|
||||
|
||||
const query = `${key}${argumentsToSDL(args)}: ${type}`;
|
||||
return applyMetadatas(query, configurations[key]);
|
||||
}
|
||||
})
|
||||
.join('\n');
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an object of arguments into graphql SDL
|
||||
* @param {object} args arguments
|
||||
* @returns {string}
|
||||
*/
|
||||
const argumentsToSDL = args => {
|
||||
if (_.isEmpty(args)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const sdlArgs = Object.entries(args)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join(', ');
|
||||
|
||||
return `(${sdlArgs})`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies description and deprecated to a field definition
|
||||
* @param {string} definition field definition
|
||||
|
||||
@ -12,8 +12,8 @@ const _ = require('lodash');
|
||||
const graphql = require('graphql');
|
||||
const PublicationState = require('../types/publication-state');
|
||||
const Types = require('./type-builder');
|
||||
const { buildModels } = require('./type-definitions');
|
||||
const { mergeSchemas, createDefaultSchema, diffResolvers } = require('./utils');
|
||||
const buildShadowCrud = require('./shadow-crud');
|
||||
const { createDefaultSchema, diffResolvers } = require('./utils');
|
||||
const { toSDL } = require('./schema-definitions');
|
||||
const { buildQuery, buildMutation } = require('./resolvers-builder');
|
||||
|
||||
@ -27,11 +27,15 @@ const generateSchema = () => {
|
||||
const isFederated = _.get(strapi.plugins.graphql.config, 'federation', false);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@ -73,17 +77,19 @@ const generateSchema = () => {
|
||||
${Types.addInput()}
|
||||
|
||||
${PublicationState.definition}
|
||||
|
||||
|
||||
type AdminUser {
|
||||
id: ID!
|
||||
username: String
|
||||
firstname: String!
|
||||
lastname: String!
|
||||
}
|
||||
|
||||
type Query {
|
||||
${queryFields}
|
||||
${query}
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
${mutationFields}
|
||||
${mutation}
|
||||
@ -133,14 +139,6 @@ const writeGenerateSchema = schema => {
|
||||
return strapi.fs.writeAppFile('exports/graphql/schema.graphql', printSchema);
|
||||
};
|
||||
|
||||
const buildModelsShadowCRUD = () => {
|
||||
const models = Object.values(strapi.contentTypes).filter(model => model.plugin !== 'admin');
|
||||
|
||||
const components = Object.values(strapi.components);
|
||||
|
||||
return mergeSchemas(createDefaultSchema(), ...buildModels([...models, ...components]));
|
||||
};
|
||||
|
||||
const buildResolvers = resolvers => {
|
||||
// Transform object to only contain function.
|
||||
return Object.keys(resolvers).reduce((acc, type) => {
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* GraphQL.js service
|
||||
*
|
||||
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
const { contentTypes } = require('strapi-utils');
|
||||
|
||||
@ -18,16 +12,46 @@ const DynamicZoneScalar = require('../types/dynamiczoneScalar');
|
||||
|
||||
const { formatModelConnectionsGQL } = require('./build-aggregation');
|
||||
const types = require('./type-builder');
|
||||
const { mergeSchemas, convertToParams, convertToQuery, amountLimiting } = require('./utils');
|
||||
const {
|
||||
actionExists,
|
||||
mergeSchemas,
|
||||
convertToParams,
|
||||
convertToQuery,
|
||||
amountLimiting,
|
||||
createDefaultSchema,
|
||||
} = require('./utils');
|
||||
const { toSDL, getTypeDescription } = require('./schema-definitions');
|
||||
const { toSingular, toPlural } = require('./naming');
|
||||
const { buildQuery, buildMutation } = require('./resolvers-builder');
|
||||
const { actionExists } = require('./utils');
|
||||
|
||||
const OPTIONS = Symbol();
|
||||
|
||||
const FIND_QUERY_ARGUMENTS = `(sort: String, limit: Int, start: Int, where: JSON, publicationState: PublicationState)`;
|
||||
const FIND_ONE_QUERY_ARGUMENTS = `(id: ID!, publicationState: PublicationState)`;
|
||||
const FIND_QUERY_ARGUMENTS = {
|
||||
sort: 'String',
|
||||
limit: 'Int',
|
||||
start: 'Int',
|
||||
where: 'JSON',
|
||||
publicationState: 'PublicationState',
|
||||
};
|
||||
|
||||
const FIND_ONE_QUERY_ARGUMENTS = {
|
||||
id: 'ID!',
|
||||
publicationState: 'PublicationState',
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a graphql schema from all the contentTypes & components loaded
|
||||
* @param {{ schema: object }} ctx
|
||||
* @returns {object}
|
||||
*/
|
||||
const buildShadowCrud = ctx => {
|
||||
const models = Object.values(strapi.contentTypes).filter(model => model.plugin !== 'admin');
|
||||
const components = Object.values(strapi.components);
|
||||
|
||||
const allSchemas = buildModels([...models, ...components], ctx);
|
||||
|
||||
return mergeSchemas(createDefaultSchema(), ...allSchemas);
|
||||
};
|
||||
|
||||
const assignOptions = (element, parent) => {
|
||||
if (Array.isArray(element)) {
|
||||
@ -41,10 +65,18 @@ const isQueryEnabled = (schema, name) => {
|
||||
return _.get(schema, `resolver.Query.${name}`) !== false;
|
||||
};
|
||||
|
||||
const getQueryInfo = (schema, name) => {
|
||||
return _.get(schema, `resolver.Query.${name}`, {});
|
||||
};
|
||||
|
||||
const isMutationEnabled = (schema, name) => {
|
||||
return _.get(schema, `resolver.Mutation.${name}`) !== false;
|
||||
};
|
||||
|
||||
const getMutationInfo = (schema, name) => {
|
||||
return _.get(schema, `resolver.Mutation.${name}`, {});
|
||||
};
|
||||
|
||||
const isNotPrivate = _.curry((model, attributeName) => {
|
||||
return !contentTypes.isPrivateAttribute(model, attributeName);
|
||||
});
|
||||
@ -294,19 +326,19 @@ const buildAssocResolvers = model => {
|
||||
*
|
||||
* @return Object
|
||||
*/
|
||||
const buildModels = models => {
|
||||
const buildModels = (models, ctx) => {
|
||||
return models.map(model => {
|
||||
const { kind, modelType } = model;
|
||||
|
||||
if (modelType === 'component') {
|
||||
return buildComponent(model);
|
||||
return buildComponent(model, ctx);
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case 'singleType':
|
||||
return buildSingleType(model);
|
||||
return buildSingleType(model, ctx);
|
||||
default:
|
||||
return buildCollectionType(model);
|
||||
return buildCollectionType(model, ctx);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -353,14 +385,12 @@ const buildComponent = component => {
|
||||
return schema;
|
||||
};
|
||||
|
||||
const buildSingleType = model => {
|
||||
const buildSingleType = (model, ctx) => {
|
||||
const { uid, modelName } = model;
|
||||
|
||||
const singularName = toSingular(modelName);
|
||||
|
||||
const _schema = _.cloneDeep(_.get(strapi.plugins, 'graphql.config._schema.graphql', {}));
|
||||
|
||||
const globalType = _.get(_schema, `type.${model.globalId}`, {});
|
||||
const globalType = _.get(ctx.schema, `type.${model.globalId}`, {});
|
||||
|
||||
const localSchema = buildModelDefinition(model, globalType);
|
||||
|
||||
@ -369,22 +399,32 @@ const buildSingleType = model => {
|
||||
return localSchema;
|
||||
}
|
||||
|
||||
if (isQueryEnabled(_schema, singularName)) {
|
||||
const resolver = buildQuery(singularName, {
|
||||
if (isQueryEnabled(ctx.schema, singularName)) {
|
||||
const resolverOpts = {
|
||||
resolver: `${uid}.find`,
|
||||
..._.get(_schema, `resolver.Query.${singularName}`, {}),
|
||||
});
|
||||
...getQueryInfo(ctx.schema, singularName),
|
||||
};
|
||||
|
||||
_.merge(localSchema, {
|
||||
const resolver = buildQuery(singularName, resolverOpts);
|
||||
|
||||
const query = {
|
||||
query: {
|
||||
[`${singularName}(publicationState: PublicationState)`]: model.globalId,
|
||||
[singularName]: {
|
||||
args: {
|
||||
publicationState: 'PublicationState',
|
||||
...(resolverOpts.args || {}),
|
||||
},
|
||||
type: model.globalId,
|
||||
},
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
[singularName]: wrapPublicationStateResolver(resolver),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
_.merge(localSchema, query);
|
||||
}
|
||||
|
||||
// Add model Input definition.
|
||||
@ -392,7 +432,7 @@ const buildSingleType = model => {
|
||||
|
||||
// build every mutation
|
||||
['update', 'delete'].forEach(action => {
|
||||
const mutationSchema = buildMutationTypeDef({ model, action }, { _schema });
|
||||
const mutationSchema = buildMutationTypeDef({ model, action }, ctx);
|
||||
|
||||
mergeSchemas(localSchema, mutationSchema);
|
||||
});
|
||||
@ -400,15 +440,13 @@ const buildSingleType = model => {
|
||||
return localSchema;
|
||||
};
|
||||
|
||||
const buildCollectionType = model => {
|
||||
const buildCollectionType = (model, ctx) => {
|
||||
const { plugin, modelName, uid } = model;
|
||||
|
||||
const singularName = toSingular(modelName);
|
||||
const pluralName = toPlural(modelName);
|
||||
|
||||
const _schema = _.cloneDeep(_.get(strapi.plugins, 'graphql.config._schema.graphql', {}));
|
||||
|
||||
const globalType = _.get(_schema, `type.${model.globalId}`, {});
|
||||
const globalType = _.get(ctx.schema, `type.${model.globalId}`, {});
|
||||
|
||||
const localSchema = buildModelDefinition(model, globalType);
|
||||
const { typeDefObj } = localSchema;
|
||||
@ -418,46 +456,65 @@ const buildCollectionType = model => {
|
||||
return localSchema;
|
||||
}
|
||||
|
||||
if (isQueryEnabled(_schema, singularName)) {
|
||||
if (isQueryEnabled(ctx.schema, singularName)) {
|
||||
const resolverOpts = {
|
||||
resolver: `${uid}.findOne`,
|
||||
..._.get(_schema, `resolver.Query.${singularName}`, {}),
|
||||
...getQueryInfo(ctx.schema, singularName),
|
||||
};
|
||||
|
||||
if (actionExists(resolverOpts)) {
|
||||
const resolver = buildQuery(singularName, resolverOpts);
|
||||
_.merge(localSchema, {
|
||||
|
||||
const query = {
|
||||
query: {
|
||||
// TODO: support all the unique fields
|
||||
[`${singularName}${FIND_ONE_QUERY_ARGUMENTS}`]: model.globalId,
|
||||
[singularName]: {
|
||||
args: {
|
||||
...FIND_ONE_QUERY_ARGUMENTS,
|
||||
...(resolverOpts.args || {}),
|
||||
},
|
||||
type: model.globalId,
|
||||
},
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
[singularName]: wrapPublicationStateResolver(resolver),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
_.merge(localSchema, query);
|
||||
}
|
||||
}
|
||||
|
||||
if (isQueryEnabled(_schema, pluralName)) {
|
||||
if (isQueryEnabled(ctx.schema, pluralName)) {
|
||||
const resolverOpts = {
|
||||
resolver: `${uid}.find`,
|
||||
..._.get(_schema, `resolver.Query.${pluralName}`, {}),
|
||||
...getQueryInfo(ctx.schema, pluralName),
|
||||
};
|
||||
|
||||
if (actionExists(resolverOpts)) {
|
||||
const resolver = buildQuery(pluralName, resolverOpts);
|
||||
_.merge(localSchema, {
|
||||
|
||||
const query = {
|
||||
query: {
|
||||
[`${pluralName}${FIND_QUERY_ARGUMENTS}`]: `[${model.globalId}]`,
|
||||
[pluralName]: {
|
||||
args: {
|
||||
...FIND_QUERY_ARGUMENTS,
|
||||
...(resolverOpts.args || {}),
|
||||
},
|
||||
type: `[${model.globalId}]`,
|
||||
},
|
||||
},
|
||||
resolvers: {
|
||||
Query: {
|
||||
[pluralName]: wrapPublicationStateResolver(resolver),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
if (isQueryEnabled(_schema, `${pluralName}Connection`)) {
|
||||
_.merge(localSchema, query);
|
||||
|
||||
if (isQueryEnabled(ctx.schema, `${pluralName}Connection`)) {
|
||||
// Generate the aggregation for the given model
|
||||
const aggregationSchema = formatModelConnectionsGQL({
|
||||
fields: typeDefObj,
|
||||
@ -477,7 +534,7 @@ const buildCollectionType = model => {
|
||||
|
||||
// build every mutation
|
||||
['create', 'update', 'delete'].forEach(action => {
|
||||
const mutationSchema = buildMutationTypeDef({ model, action }, { _schema });
|
||||
const mutationSchema = buildMutationTypeDef({ model, action }, ctx);
|
||||
mergeSchemas(localSchema, mutationSchema);
|
||||
});
|
||||
|
||||
@ -487,14 +544,14 @@ const buildCollectionType = model => {
|
||||
// TODO:
|
||||
// - Implement batch methods (need to update the content-manager as well).
|
||||
// - Implement nested transactional methods (create/update).
|
||||
const buildMutationTypeDef = ({ model, action }, { _schema }) => {
|
||||
const buildMutationTypeDef = ({ model, action }, ctx) => {
|
||||
const capitalizedName = _.upperFirst(toSingular(model.modelName));
|
||||
const mutationName = `${action}${capitalizedName}`;
|
||||
|
||||
const resolverOpts = {
|
||||
resolver: `${model.uid}.${action}`,
|
||||
transformOutput: result => ({ [toSingular(model.modelName)]: result }),
|
||||
..._.get(_schema, `resolver.Mutation.${mutationName}`, {}),
|
||||
...getMutationInfo(ctx.schema, mutationName),
|
||||
};
|
||||
|
||||
if (!actionExists(resolverOpts)) {
|
||||
@ -509,7 +566,7 @@ const buildMutationTypeDef = ({ model, action }, { _schema }) => {
|
||||
});
|
||||
|
||||
// ignore if disabled
|
||||
if (!isMutationEnabled(_schema, mutationName)) {
|
||||
if (!isMutationEnabled(ctx.schema, mutationName)) {
|
||||
return {
|
||||
definition,
|
||||
};
|
||||
@ -517,15 +574,24 @@ const buildMutationTypeDef = ({ model, action }, { _schema }) => {
|
||||
|
||||
const { kind } = model;
|
||||
|
||||
let mutationDef = `${mutationName}(input: ${mutationName}Input)`;
|
||||
if (kind === 'singleType' && action === 'delete') {
|
||||
mutationDef = mutationName;
|
||||
const args = {};
|
||||
|
||||
if (kind !== 'singleType' || action !== 'delete') {
|
||||
Object.assign(args, {
|
||||
input: `${mutationName}Input`,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
definition,
|
||||
mutation: {
|
||||
[mutationDef]: `${mutationName}Payload`,
|
||||
[mutationName]: {
|
||||
args: {
|
||||
...args,
|
||||
...(resolverOpts.args || {}),
|
||||
},
|
||||
type: `${mutationName}Payload`,
|
||||
},
|
||||
},
|
||||
resolvers: {
|
||||
Mutation: {
|
||||
@ -535,6 +601,4 @@ const buildMutationTypeDef = ({ model, action }, { _schema }) => {
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
buildModels,
|
||||
};
|
||||
module.exports = buildShadowCrud;
|
||||
@ -4,24 +4,39 @@ const _ = require('lodash');
|
||||
const { QUERY_OPERATORS } = require('strapi-utils');
|
||||
|
||||
/**
|
||||
* Merges
|
||||
* @typedef {object} Schema
|
||||
* @property {object} resolvers
|
||||
* @property {object} mutation
|
||||
* @property {object} query
|
||||
* @property {string} definition
|
||||
*/
|
||||
const mergeSchemas = (root, ...subs) => {
|
||||
subs.forEach(sub => {
|
||||
|
||||
/**
|
||||
* Merges strapi graphql schema together
|
||||
* @param {Schema} object - destination object
|
||||
* @param {Schema[]} sources - source objects to merge into the destination object
|
||||
* @returns {Schema}
|
||||
*/
|
||||
const mergeSchemas = (object, ...sources) => {
|
||||
sources.forEach(sub => {
|
||||
if (_.isEmpty(sub)) return;
|
||||
const { definition = '', query = {}, mutation = {}, resolvers = {} } = sub;
|
||||
|
||||
root.definition += '\n' + definition;
|
||||
_.merge(root, {
|
||||
object.definition += '\n' + definition;
|
||||
_.merge(object, {
|
||||
query,
|
||||
mutation,
|
||||
resolvers,
|
||||
});
|
||||
});
|
||||
|
||||
return root;
|
||||
return object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an empty schema
|
||||
* @returns {Schema}
|
||||
*/
|
||||
const createDefaultSchema = () => ({
|
||||
definition: '',
|
||||
query: {},
|
||||
|
||||
@ -205,6 +205,20 @@ const addCreateLocalizationAction = contentType => {
|
||||
_.set(strapi, coreApiControllerPath, handler);
|
||||
};
|
||||
|
||||
const mergeCustomizer = (dest, src) => {
|
||||
if (typeof dest === 'string') {
|
||||
return `${dest}\n${src}`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a graphql schema to the plugin's global graphl schema to be processed
|
||||
* @param {object} schema
|
||||
*/
|
||||
const addGraphqlSchema = schema => {
|
||||
_.mergeWith(strapi.plugins.i18n.config.schema.graphql, schema, mergeCustomizer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add localization mutation & filters to use with the graphql plugin
|
||||
* @param {object} contentType
|
||||
@ -216,27 +230,61 @@ const addGraphqlLocalizationAction = contentType => {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeName = _.capitalize(modelName);
|
||||
_.mergeWith(
|
||||
strapi.plugins.i18n.config.schema.graphql,
|
||||
{
|
||||
mutation: `
|
||||
create${typeName}Localization(input: update${typeName}Input!): ${typeName}!
|
||||
`,
|
||||
const { toSingular, toPlural } = strapi.plugins.graphql.services.naming;
|
||||
|
||||
// We use a string instead of an enum as the locales can be changed in the admin
|
||||
// NOTE: We could use a custom scalar so the validation becomes dynamic
|
||||
const localeArgs = {
|
||||
args: {
|
||||
locale: 'String',
|
||||
},
|
||||
};
|
||||
|
||||
// add locale arguments in the existing queries
|
||||
if (isSingleType(contentType)) {
|
||||
const queryName = toSingular(modelName);
|
||||
const mutationSuffix = _.upperFirst(queryName);
|
||||
|
||||
addGraphqlSchema({
|
||||
resolver: {
|
||||
Query: {
|
||||
[queryName]: localeArgs,
|
||||
},
|
||||
Mutation: {
|
||||
[`create${typeName}Localization`]: {
|
||||
resolver: `application::${modelName}.${modelName}.createLocalization`,
|
||||
},
|
||||
[`update${mutationSuffix}`]: localeArgs,
|
||||
[`delete${mutationSuffix}`]: localeArgs,
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const queryName = toPlural(modelName);
|
||||
|
||||
addGraphqlSchema({
|
||||
resolver: {
|
||||
Query: {
|
||||
[queryName]: localeArgs,
|
||||
[`${queryName}Connection`]: localeArgs,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// add new mutation to create a localization
|
||||
const typeName = _.capitalize(modelName);
|
||||
const mutationName = `create${typeName}Localization`;
|
||||
const mutationDef = `${mutationName}(input: update${typeName}Input!): ${typeName}!`;
|
||||
const actionName = `${contentType.uid}.createLocalization`;
|
||||
|
||||
addGraphqlSchema({
|
||||
mutation: mutationDef,
|
||||
resolver: {
|
||||
Mutation: {
|
||||
[mutationName]: {
|
||||
resolver: actionName,
|
||||
},
|
||||
},
|
||||
},
|
||||
(dest, src) => {
|
||||
if (typeof dest === 'string') {
|
||||
return `${dest}\n${src}`;
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user