mirror of
https://github.com/strapi/strapi.git
synced 2025-10-18 11:32:42 +00:00
V4/graphql configuration (#10896)
* Use a scalar to register the i18n locale arg * Remove useless files & comments * Use custom config for apollo server & the pagination (better handling of pagination) * Fix missing strapi variable being transmitted to wrapResolvers/parseMiddlewares * PR review comments
This commit is contained in:
parent
e2be869d3b
commit
2b715a6ee9
@ -1,13 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
module.exports = ({ env }) => ({
|
||||
module.exports = () => ({
|
||||
graphql: {
|
||||
enabled: true,
|
||||
config: {
|
||||
amountLimit: 50,
|
||||
depthLimit: 10,
|
||||
endpoint: '/graphql',
|
||||
|
||||
defaultLimit: 25,
|
||||
maxLimit: 100,
|
||||
|
||||
apolloServer: {
|
||||
tracing: true,
|
||||
},
|
||||
|
@ -15,21 +15,36 @@ const STRAPI_DEFAULTS = {
|
||||
|
||||
const paginationAttributes = ['start', 'limit', 'page', 'pageSize'];
|
||||
|
||||
const withMaxLimit = (limit, maxLimit = -1) => {
|
||||
if (maxLimit === -1 || limit < maxLimit) {
|
||||
return limit;
|
||||
}
|
||||
|
||||
return maxLimit;
|
||||
};
|
||||
|
||||
// Ensure minimum page & pageSize values (page >= 1, pageSize >= 0, start >= 0, limit >= 0)
|
||||
const ensureMinValues = ({ start, limit }) => ({
|
||||
start: Math.max(start, 0),
|
||||
limit: Math.max(limit, 0),
|
||||
limit: Math.max(limit, 1),
|
||||
});
|
||||
|
||||
const withDefaultPagination = (args, defaults = {}) => {
|
||||
const ensureMaxValues = (maxLimit = -1) => ({ start, limit }) => ({
|
||||
start: Math.min(start, limit),
|
||||
limit: withMaxLimit(limit, maxLimit),
|
||||
});
|
||||
|
||||
const withDefaultPagination = (args, { defaults = {}, maxLimit = -1 } = {}) => {
|
||||
const defaultValues = merge(STRAPI_DEFAULTS, defaults);
|
||||
|
||||
const usePagePagination = !isNil(args.page) || !isNil(args.pageSize);
|
||||
const useOffsetPagination = !isNil(args.start) || !isNil(args.limit);
|
||||
|
||||
const ensureValidValues = pipe(ensureMinValues, ensureMaxValues(maxLimit));
|
||||
|
||||
// If there is no pagination attribute, don't modify the payload
|
||||
if (!usePagePagination && !useOffsetPagination) {
|
||||
return merge(args, defaultValues.offset);
|
||||
return merge(args, ensureValidValues(defaultValues.offset));
|
||||
}
|
||||
|
||||
// If there is page & offset pagination attributes, throw an error
|
||||
@ -59,8 +74,8 @@ const withDefaultPagination = (args, defaults = {}) => {
|
||||
const replacePaginationAttributes = pipe(
|
||||
// Remove pagination attributes
|
||||
omit(paginationAttributes),
|
||||
// Merge the object with the new pagination + ensure minimum values (page >= 1, pageSize >= 0)
|
||||
merge(ensureMinValues(pagination))
|
||||
// Merge the object with the new pagination + ensure minimum & maximum values
|
||||
merge(ensureValidValues(pagination))
|
||||
);
|
||||
|
||||
return replacePaginationAttributes(args);
|
||||
|
33
packages/plugins/graphql/server/bootstrap.js
vendored
33
packages/plugins/graphql/server/bootstrap.js
vendored
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { isEmpty, getOr } = require('lodash/fp');
|
||||
const { isEmpty, mergeWith, isArray } = require('lodash/fp');
|
||||
const { ApolloServer } = require('apollo-server-koa');
|
||||
const {
|
||||
ApolloServerPluginLandingPageLocalDefault,
|
||||
@ -9,6 +9,12 @@ const {
|
||||
const depthLimit = require('graphql-depth-limit');
|
||||
const { graphqlUploadKoa } = require('graphql-upload');
|
||||
|
||||
const merge = mergeWith((a, b) => {
|
||||
if (isArray(a) && isArray(b)) {
|
||||
return a.concat(b);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = async strapi => {
|
||||
// Generate the GraphQL schema for the content API
|
||||
const schema = strapi
|
||||
@ -22,10 +28,9 @@ module.exports = async strapi => {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = getOr({}, 'config', strapi.plugin('graphql'));
|
||||
const apolloServerConfig = getOr({}, 'apolloServer', config);
|
||||
const { config } = strapi.plugin('graphql');
|
||||
|
||||
const serverParams = {
|
||||
const defaultServerConfig = {
|
||||
// Schema
|
||||
schema,
|
||||
|
||||
@ -40,13 +45,8 @@ module.exports = async strapi => {
|
||||
return ctx;
|
||||
},
|
||||
|
||||
// Format & validation
|
||||
formatError: err => {
|
||||
const formatError = getOr(null, 'formatError', config);
|
||||
|
||||
return typeof formatError === 'function' ? formatError(err) : err;
|
||||
},
|
||||
validationRules: [depthLimit(config.depthLimit)],
|
||||
// Validation
|
||||
validationRules: [depthLimit(config('depthLimit'))],
|
||||
|
||||
// Misc
|
||||
cors: false,
|
||||
@ -59,11 +59,12 @@ module.exports = async strapi => {
|
||||
? ApolloServerPluginLandingPageLocalDefault({ footer: false })
|
||||
: ApolloServerPluginLandingPageProductionDefault({ footer: false }),
|
||||
],
|
||||
...apolloServerConfig,
|
||||
};
|
||||
|
||||
const serverConfig = merge(defaultServerConfig, config('apolloServer', {}));
|
||||
|
||||
// Create a new Apollo server
|
||||
const server = new ApolloServer(serverParams);
|
||||
const server = new ApolloServer(serverConfig);
|
||||
|
||||
// Register the upload middleware
|
||||
useUploadMiddleware(strapi, config);
|
||||
@ -78,7 +79,7 @@ module.exports = async strapi => {
|
||||
// Link the Apollo server & the Strapi app
|
||||
server.applyMiddleware({
|
||||
app: strapi.app,
|
||||
path: config.endpoint,
|
||||
path: config('endpoint', '/graphql'),
|
||||
});
|
||||
|
||||
// Register destroy behavior
|
||||
@ -92,13 +93,13 @@ module.exports = async strapi => {
|
||||
/**
|
||||
* Register the upload middleware powered by graphql-upload in Strapi
|
||||
* @param {object} strapi
|
||||
* @param {object} config
|
||||
* @param {function} config
|
||||
*/
|
||||
const useUploadMiddleware = (strapi, config) => {
|
||||
const uploadMiddleware = graphqlUploadKoa();
|
||||
|
||||
strapi.app.use((ctx, next) => {
|
||||
if (ctx.path === config.endpoint) {
|
||||
if (ctx.path === config('endpoint')) {
|
||||
return uploadMiddleware(ctx, next);
|
||||
}
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"endpoint": "/graphql",
|
||||
"shadowCRUD": true,
|
||||
"playgroundAlways": false,
|
||||
"depthLimit": 7,
|
||||
"amountLimit": 100,
|
||||
"shareEnabled": false,
|
||||
"federation": false,
|
||||
"apolloServer": {
|
||||
"tracing": false
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line node/no-extraneous-require
|
||||
const loadUtils = require('@strapi/strapi/lib/load');
|
||||
const _ = require('lodash');
|
||||
|
||||
const loadApisGraphqlConfig = appPath =>
|
||||
loadUtils.loadFiles(appPath, 'api/**/config/*.graphql?(.js)');
|
||||
|
||||
const loadPluginsGraphqlConfig = async installedPlugins => {
|
||||
const root = {};
|
||||
|
||||
for (let pluginName of installedPlugins) {
|
||||
const pluginDir = loadUtils.findPackagePath(`@strapi/plugin-${pluginName}`);
|
||||
|
||||
const result = await loadUtils.loadFiles(pluginDir, 'config/*.graphql?(.js)');
|
||||
|
||||
_.set(root, ['plugins', pluginName], result);
|
||||
}
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
const loadLocalPluginsGraphqlConfig = async appPath =>
|
||||
loadUtils.loadFiles(appPath, 'plugins/**/config/*.graphql?(.js)');
|
||||
|
||||
const loadExtensions = async appPath =>
|
||||
loadUtils.loadFiles(appPath, 'extensions/**/config/*.graphql?(.js)');
|
||||
|
||||
/**
|
||||
* Loads the graphql config files
|
||||
*/
|
||||
module.exports = async ({ appPath, installedPlugins }) => {
|
||||
const [apis, plugins, localPlugins, extensions] = await Promise.all([
|
||||
loadApisGraphqlConfig(appPath),
|
||||
loadPluginsGraphqlConfig(installedPlugins),
|
||||
loadLocalPluginsGraphqlConfig(appPath),
|
||||
loadExtensions(appPath),
|
||||
]);
|
||||
|
||||
return _.merge({}, apis, plugins, extensions, localPlugins);
|
||||
};
|
@ -1,77 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// const _ = require('lodash');
|
||||
// const loadConfigs = require('./load-config');
|
||||
//
|
||||
// const attachMetadataToResolvers = (schema, { api, plugin }) => {
|
||||
// const { resolver = {} } = schema;
|
||||
// if (_.isEmpty(resolver)) return schema;
|
||||
//
|
||||
// Object.keys(resolver).forEach(type => {
|
||||
// if (!_.isPlainObject(resolver[type])) return;
|
||||
//
|
||||
// Object.keys(resolver[type]).forEach(resolverName => {
|
||||
// if (!_.isPlainObject(resolver[type][resolverName])) return;
|
||||
//
|
||||
// resolver[type][resolverName]['_metadatas'] = {
|
||||
// api,
|
||||
// plugin,
|
||||
// };
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// return schema;
|
||||
// };
|
||||
|
||||
// todo[v4]: Rework how we load additional gql schema / customize current schema
|
||||
module.exports = async (/*{ strapi }*/) => {
|
||||
// const { appPath, installedPlugins } = strapi.config;
|
||||
//
|
||||
// // Load core utils.
|
||||
//
|
||||
// const { api, plugins, extensions } = await loadConfigs({
|
||||
// appPath,
|
||||
// installedPlugins,
|
||||
// });
|
||||
//
|
||||
// _.merge(strapi, { api, plugins });
|
||||
//
|
||||
// // 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 });
|
||||
// });
|
||||
//
|
||||
// const pluginsSchemas = Object.keys(strapi.plugins || {}).map(key => {
|
||||
// const schema = _.get(strapi.plugins[key], 'config.schema.graphql', {});
|
||||
// return attachMetadataToResolvers(schema, { plugin: key });
|
||||
// });
|
||||
//
|
||||
// const extensionsSchemas = Object.keys(extensions || {}).map(key => {
|
||||
// const schema = _.get(extensions[key], 'config.schema.graphql', {});
|
||||
// return attachMetadataToResolvers(schema, { plugin: key });
|
||||
// });
|
||||
//
|
||||
// const baseSchema = mergeSchemas([...pluginsSchemas, ...extensionsSchemas, ...apisSchemas]);
|
||||
//
|
||||
// // save the final schema in the plugin's config
|
||||
// _.set(strapi.plugins.graphql, 'config._schema.graphql', baseSchema);
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges a list of schemas
|
||||
* @param {Array<Object>} schemas - The list of schemas to merge
|
||||
*/
|
||||
// const mergeSchemas = schemas => {
|
||||
// return schemas.reduce((acc, el) => {
|
||||
// const { definition, query, mutation, type, resolver } = el;
|
||||
//
|
||||
// return _.merge(acc, {
|
||||
// definition: `${acc.definition || ''} ${definition || ''}`,
|
||||
// query: `${acc.query || ''} ${query || ''}`,
|
||||
// mutation: `${acc.mutation || ''} ${mutation || ''}`,
|
||||
// type,
|
||||
// resolver,
|
||||
// });
|
||||
// }, {});
|
||||
// };
|
@ -8,7 +8,7 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
return {
|
||||
/**
|
||||
* Build a higher level type for a content type which contains both the attributes, the ID and the metadata
|
||||
* Build a higher level type for a content type which contains the attributes, the ID and the metadata
|
||||
* @param {object} contentType The content type which will be used to build its entity type
|
||||
* @return {NexusObjectTypeDef}
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@ const {
|
||||
} = require('@strapi/utils');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
const getGraphQLService = strapi.plugin('graphql').service;
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
return {
|
||||
/**
|
||||
@ -18,8 +18,8 @@ module.exports = ({ strapi }) => {
|
||||
* @return {object}
|
||||
*/
|
||||
getContentTypeArgs(contentType, { multiple = true } = {}) {
|
||||
const { naming } = getGraphQLService('utils');
|
||||
const { args } = getGraphQLService('internals');
|
||||
const { naming } = getService('utils');
|
||||
const { args } = getService('internals');
|
||||
|
||||
const { kind, modelType } = contentType;
|
||||
|
||||
@ -71,7 +71,7 @@ module.exports = ({ strapi }) => {
|
||||
* @return {Object<string, object>}
|
||||
*/
|
||||
getUniqueScalarAttributes: attributes => {
|
||||
const { isStrapiScalar } = getGraphQLService('utils').attributes;
|
||||
const { isStrapiScalar } = getService('utils').attributes;
|
||||
|
||||
const uniqueAttributes = entries(attributes).filter(
|
||||
([, attribute]) => isStrapiScalar(attribute) && attribute.unique
|
||||
@ -86,7 +86,7 @@ module.exports = ({ strapi }) => {
|
||||
* @return {Object<string, string>}
|
||||
*/
|
||||
scalarAttributesToFiltersMap: mapValues(attribute => {
|
||||
const { mappers, naming } = getGraphQLService('utils');
|
||||
const { mappers, naming } = getService('utils');
|
||||
|
||||
const gqlScalar = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
||||
|
||||
@ -97,7 +97,7 @@ module.exports = ({ strapi }) => {
|
||||
* Apply basic transform to GQL args
|
||||
*/
|
||||
transformArgs(args, { contentType, usePagination = false } = {}) {
|
||||
const { mappers } = getGraphQLService('utils');
|
||||
const { mappers } = getService('utils');
|
||||
const { pagination = {}, filters = {} } = args;
|
||||
|
||||
// Init
|
||||
@ -105,9 +105,18 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
// Pagination
|
||||
if (usePagination) {
|
||||
const defaultLimit = strapi.plugin('graphql').config('defaultLimit');
|
||||
const maxLimit = strapi.plugin('graphql').config('maxLimit', -1);
|
||||
|
||||
Object.assign(
|
||||
newArgs,
|
||||
withDefaultPagination(pagination /*, config.get(graphql.pagination.defaults)*/)
|
||||
withDefaultPagination(pagination, {
|
||||
maxLimit,
|
||||
defaults: {
|
||||
offset: { limit: defaultLimit },
|
||||
page: { pageSize: defaultLimit },
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,11 @@ const KINDS = {
|
||||
|
||||
const GRAPHQL_SCALAR_OPERATORS = {
|
||||
// ID
|
||||
ID: ['eq', 'not'],
|
||||
ID: ['eq', 'not', 'gt', 'lt'],
|
||||
// Booleans
|
||||
Boolean: ['eq', 'not'],
|
||||
// Strings
|
||||
String: ['eq', 'not', 'contains', 'startsWith', 'endsWith'],
|
||||
String: ['eq', 'not', 'gt', 'lt', 'contains', 'startsWith', 'endsWith'],
|
||||
// Numbers
|
||||
Int: ['eq', 'not', 'gt', 'lt'],
|
||||
Long: ['eq', 'not', 'gt', 'lt'],
|
||||
|
@ -76,7 +76,7 @@ module.exports = ({ strapi }) => {
|
||||
// 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
|
||||
schema => wrapResolvers({ schema, extension })
|
||||
schema => wrapResolvers({ schema, strapi, extension })
|
||||
)({ registry, extension });
|
||||
};
|
||||
|
||||
|
@ -9,10 +9,11 @@ const { createPoliciesMiddleware } = require('./policy');
|
||||
* customized using the GraphQL extension service
|
||||
* @param {object} options
|
||||
* @param {GraphQLSchema} options.schema
|
||||
* @param {object} options.strapi
|
||||
* @param {object} options.extension
|
||||
* @return {GraphQLSchema}
|
||||
*/
|
||||
const wrapResolvers = ({ schema, extension = {} }) => {
|
||||
const wrapResolvers = ({ schema, strapi, extension = {} }) => {
|
||||
// Get all the registered resolvers configuration
|
||||
const { resolversConfig = {} } = extension;
|
||||
|
||||
@ -43,7 +44,7 @@ const wrapResolvers = ({ schema, extension = {} }) => {
|
||||
|
||||
const { resolve: baseResolver = get(fieldName) } = fieldDefinition;
|
||||
|
||||
const middlewares = parseMiddlewares(resolverConfig);
|
||||
const middlewares = parseMiddlewares(resolverConfig, strapi);
|
||||
|
||||
// Generate the policy middleware
|
||||
const policyMiddleware = createPoliciesMiddleware(resolverConfig, { strapi });
|
||||
@ -84,9 +85,10 @@ const wrapResolvers = ({ schema, extension = {} }) => {
|
||||
/**
|
||||
* Get & parse middlewares definitions from the resolver's config
|
||||
* @param {object} resolverConfig
|
||||
* @param {object} strapi
|
||||
* @return {function[]}
|
||||
*/
|
||||
const parseMiddlewares = resolverConfig => {
|
||||
const parseMiddlewares = (resolverConfig, strapi) => {
|
||||
const resolverMiddlewares = getOr([], 'middlewares', resolverConfig);
|
||||
|
||||
// TODO: [v4] to factorize with compose endpoints (routes)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
const { camelCase, upperFirst, lowerFirst, pipe, get } = require('lodash/fp');
|
||||
|
||||
const { toSingular, toPlural } = require('../old/naming');
|
||||
const { toSingular } = require('../old/naming');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
/**
|
||||
@ -23,15 +23,20 @@ module.exports = ({ strapi }) => {
|
||||
/**
|
||||
* Build the base type name for a given content type
|
||||
* @param {object} contentType
|
||||
* @param {object} options
|
||||
* @param {'singular' | 'plural'} options.plurality
|
||||
* @return {string}
|
||||
*/
|
||||
const getTypeName = contentType => {
|
||||
const getTypeName = (contentType, { plurality = 'singular' } = {}) => {
|
||||
const plugin = get('plugin', contentType);
|
||||
const modelName = get('modelName', contentType);
|
||||
const singularName = get('info.singularName', contentType);
|
||||
const name =
|
||||
plurality === 'singular'
|
||||
? get('info.singularName', contentType)
|
||||
: get('info.pluralName', contentType);
|
||||
|
||||
const transformedPlugin = upperFirst(camelCase(plugin));
|
||||
const transformedModelName = upperFirst(singularName || toSingular(modelName));
|
||||
const transformedModelName = upperFirst(camelCase(name || toSingular(modelName)));
|
||||
|
||||
return `${transformedPlugin}${transformedModelName}`;
|
||||
};
|
||||
@ -207,10 +212,10 @@ module.exports = ({ strapi }) => {
|
||||
}
|
||||
|
||||
const getCustomTypeName = pipe(
|
||||
getTypeName,
|
||||
plurality === 'plural' ? toPlural : toSingular,
|
||||
ct => getTypeName(ct, { plurality }),
|
||||
firstLetterCase === 'upper' ? upperFirst : lowerFirst
|
||||
);
|
||||
|
||||
return contentType => `${prefix}${getCustomTypeName(contentType)}${suffix}`;
|
||||
};
|
||||
|
||||
|
@ -1,20 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const bootstrap = require('./server/bootstrap');
|
||||
const register = require('./server/register');
|
||||
const services = require('./server/services');
|
||||
|
||||
module.exports = (/* strapi, config */) => {
|
||||
return {
|
||||
bootstrap,
|
||||
register,
|
||||
services,
|
||||
// destroy: () => {},
|
||||
// config: {},
|
||||
// routes: [],
|
||||
// controllers: {},
|
||||
// policies: {},
|
||||
// middlewares: {},
|
||||
// contentTypes: {},
|
||||
};
|
||||
};
|
||||
|
@ -1,47 +1,85 @@
|
||||
'use strict';
|
||||
|
||||
const { propEq, identity } = require('lodash/fp');
|
||||
|
||||
const LOCALE_SCALAR_TYPENAME = 'Locale';
|
||||
const LOCALE_ARG_PLUGIN_NAME = 'I18NLocaleArg';
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
register() {
|
||||
const useExtension = strapi
|
||||
strapi
|
||||
.plugin('graphql')
|
||||
.service('extension')
|
||||
.for('content-api').use;
|
||||
.for('content-api')
|
||||
.use(({ nexus, typeRegistry }) => {
|
||||
const i18nLocaleArgPlugin = createI18nLocaleArgPlugin({ nexus, strapi, typeRegistry });
|
||||
const i18nLocaleScalar = createLocaleScalar({ nexus, strapi });
|
||||
|
||||
const { isLocalizedContentType } = strapi.plugin('i18n').service('content-types');
|
||||
|
||||
useExtension(({ nexus, typeRegistry }) => {
|
||||
/**
|
||||
* Adds a "locale" arg to localized queries and mutations
|
||||
* @param {object} config
|
||||
*/
|
||||
const addLocaleArg = config => {
|
||||
const { parentType } = config;
|
||||
|
||||
// Only target queries or mutations
|
||||
if (parentType !== 'Query' && parentType !== 'Mutation') {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = typeRegistry.get(config.type).config.contentType;
|
||||
|
||||
// Ignore non-localized content types
|
||||
if (!isLocalizedContentType(contentType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
config.args.locale = nexus.stringArg();
|
||||
};
|
||||
|
||||
const i18nPlugin = nexus.plugin({
|
||||
name: 'i18nPlugin',
|
||||
|
||||
onAddOutputField(config) {
|
||||
// Add the locale arg to the queries on localized CTs
|
||||
addLocaleArg(config);
|
||||
},
|
||||
return {
|
||||
plugins: [i18nLocaleArgPlugin],
|
||||
types: [i18nLocaleScalar],
|
||||
};
|
||||
});
|
||||
|
||||
return { plugins: [i18nPlugin] };
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const createLocaleScalar = ({ nexus, strapi }) => {
|
||||
const locales = strapi
|
||||
.plugin('i18n')
|
||||
.service('iso-locales')
|
||||
.getIsoLocales();
|
||||
|
||||
return nexus.scalarType({
|
||||
name: LOCALE_SCALAR_TYPENAME,
|
||||
|
||||
description: 'A string used to identify an i18n locale',
|
||||
|
||||
serialize: identity,
|
||||
parseValue: identity,
|
||||
|
||||
parseLiteral(ast) {
|
||||
if (ast.kind !== 'StringValue') {
|
||||
throw new TypeError('Locale cannot represent non string type');
|
||||
}
|
||||
|
||||
const isValidLocale = locales.find(propEq('code', ast.value));
|
||||
|
||||
if (!isValidLocale) {
|
||||
throw new TypeError('Unknown locale supplied');
|
||||
}
|
||||
|
||||
return ast.value;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const createI18nLocaleArgPlugin = ({ nexus, strapi, typeRegistry }) => {
|
||||
const { isLocalizedContentType } = strapi.plugin('i18n').service('content-types');
|
||||
|
||||
const addLocaleArg = config => {
|
||||
const { parentType } = config;
|
||||
|
||||
// Only target queries or mutations
|
||||
if (parentType !== 'Query' && parentType !== 'Mutation') {
|
||||
return;
|
||||
}
|
||||
|
||||
const contentType = typeRegistry.get(config.type).config.contentType;
|
||||
|
||||
// Ignore non-localized content types
|
||||
if (!isLocalizedContentType(contentType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
config.args.locale = nexus.arg({ type: LOCALE_SCALAR_TYPENAME });
|
||||
};
|
||||
|
||||
return nexus.plugin({
|
||||
name: LOCALE_ARG_PLUGIN_NAME,
|
||||
|
||||
onAddOutputField(config) {
|
||||
// Add the locale arg to the queries on localized CTs
|
||||
addLocaleArg(config);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user