mirror of
https://github.com/strapi/strapi.git
synced 2025-11-03 11:25:17 +00:00
Disable subscriptions by default, change how to handle the type in wrapResolvers
This commit is contained in:
parent
cf5e4078b5
commit
69d8b20f1b
10
packages/plugins/graphql/server/bootstrap.js
vendored
10
packages/plugins/graphql/server/bootstrap.js
vendored
@ -31,9 +31,9 @@ module.exports = async ({ strapi }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const { config } = strapi.plugin('graphql');
|
||||
const { config } = strapi.plugin('graphql').service('utils');
|
||||
|
||||
const path = config('endpoint', '/graphql');
|
||||
const path = config.endpoint;
|
||||
|
||||
const defaultServerConfig = {
|
||||
// Schema
|
||||
@ -46,7 +46,7 @@ module.exports = async ({ strapi }) => {
|
||||
}),
|
||||
|
||||
// Validation
|
||||
validationRules: [depthLimit(config('depthLimit'))],
|
||||
validationRules: [depthLimit(config.depthLimit)],
|
||||
|
||||
// Errors
|
||||
formatError: formatGraphqlError,
|
||||
@ -63,10 +63,10 @@ module.exports = async ({ strapi }) => {
|
||||
],
|
||||
};
|
||||
|
||||
const serverConfig = merge(defaultServerConfig, config('apolloServer', {}));
|
||||
const serverConfig = merge(defaultServerConfig, config.apolloServer);
|
||||
|
||||
// Handle subscriptions
|
||||
if (config('subscriptions', true)) {
|
||||
if (config.subscriptions) {
|
||||
const subscriptionServer = SubscriptionServer.create(
|
||||
{ schema, execute, subscribe },
|
||||
{ server: strapi.server.httpServer, path }
|
||||
|
||||
@ -95,7 +95,7 @@ module.exports = ({ strapi }) => {
|
||||
* Apply basic transform to GQL args
|
||||
*/
|
||||
transformArgs(args, { contentType, usePagination = false } = {}) {
|
||||
const { mappers } = getService('utils');
|
||||
const { mappers, config } = getService('utils');
|
||||
const { pagination = {}, filters = {} } = args;
|
||||
|
||||
// Init
|
||||
@ -103,8 +103,7 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
// Pagination
|
||||
if (usePagination) {
|
||||
const defaultLimit = strapi.plugin('graphql').config('defaultLimit');
|
||||
const maxLimit = strapi.plugin('graphql').config('maxLimit', -1);
|
||||
const { defaultLimit, maxLimit } = config;
|
||||
|
||||
Object.assign(
|
||||
newArgs,
|
||||
|
||||
@ -5,6 +5,7 @@ const {
|
||||
makeExecutableSchema,
|
||||
addResolversToSchema,
|
||||
} = require('@graphql-tools/schema');
|
||||
const { pruneSchema } = require('@graphql-tools/utils');
|
||||
const { makeSchema } = require('nexus');
|
||||
const { prop, startsWith } = require('lodash/fp');
|
||||
|
||||
@ -26,7 +27,7 @@ const {
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
const { service: getGraphQLService } = strapi.plugin('graphql');
|
||||
const { config } = strapi.plugin('graphql');
|
||||
const { config } = getGraphQLService('utils');
|
||||
|
||||
const { KINDS, GENERIC_MORPH_TYPENAME } = getGraphQLService('constants');
|
||||
const extensionService = getGraphQLService('extension');
|
||||
@ -37,7 +38,7 @@ module.exports = ({ strapi }) => {
|
||||
let builders;
|
||||
|
||||
const buildSchema = () => {
|
||||
const isShadowCRUDEnabled = !!config('shadowCRUD', true);
|
||||
const isShadowCRUDEnabled = !!config.shadowCRUD;
|
||||
|
||||
// Create a new empty type registry
|
||||
registry = getGraphQLService('type-registry').new();
|
||||
@ -71,7 +72,11 @@ module.exports = ({ strapi }) => {
|
||||
// Wrap resolvers if needed (auth, middlewares, policies...) as configured in the extension
|
||||
const wrappedSchema = wrapResolvers({ schema, strapi, extension });
|
||||
|
||||
return wrappedSchema;
|
||||
// Prune schema, remove unused types
|
||||
// eg: removes registered subscriptions if they're disabled in the config)
|
||||
const prunedSchema = pruneSchema(wrappedSchema);
|
||||
|
||||
return prunedSchema;
|
||||
};
|
||||
|
||||
const buildSchemas = ({ registry }) => {
|
||||
|
||||
@ -25,100 +25,96 @@ const introspectionQueries = [
|
||||
* @return {GraphQLSchema}
|
||||
*/
|
||||
const wrapResolvers = ({ schema, strapi, extension = {} }) => {
|
||||
const { config: graphQLConfig } = strapi.plugin('graphql').service('utils');
|
||||
// Get all the registered resolvers configuration
|
||||
const { resolversConfig = {} } = extension;
|
||||
|
||||
// Fields filters
|
||||
const isValidFieldName = ([field]) => !field.startsWith('__');
|
||||
|
||||
const typesMaps = [schema.getTypeMap()];
|
||||
const typeMap = schema.getTypeMap();
|
||||
|
||||
const subscriptionType = schema.getSubscriptionType();
|
||||
if (subscriptionType) {
|
||||
typesMaps.push(subscriptionType.getFields());
|
||||
if (!graphQLConfig.subscriptions) {
|
||||
delete typeMap.Subscription;
|
||||
}
|
||||
|
||||
typesMaps.forEach(typeMap =>
|
||||
// Iterate over every field from every type within the
|
||||
// schema's type map and wrap its resolve attribute if needed
|
||||
Object.entries(typeMap).forEach(([type, definition]) => {
|
||||
const isGraphQLObjectType = definition instanceof GraphQLObjectType;
|
||||
const isIgnoredType = introspectionQueries.includes(type);
|
||||
Object.entries(typeMap).forEach(([type, definition]) => {
|
||||
const isGraphQLObjectType = definition instanceof GraphQLObjectType;
|
||||
const isIgnoredType = introspectionQueries.includes(type);
|
||||
|
||||
if (!isGraphQLObjectType || isIgnoredType) {
|
||||
return;
|
||||
}
|
||||
if (!isGraphQLObjectType || isIgnoredType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fields = definition.getFields();
|
||||
const fieldsToProcess = Object.entries(fields).filter(isValidFieldName);
|
||||
const fields = definition.getFields();
|
||||
const fieldsToProcess = Object.entries(fields).filter(isValidFieldName);
|
||||
|
||||
for (const [fieldName, fieldDefinition] of fieldsToProcess) {
|
||||
const defaultResolver = get(fieldName);
|
||||
for (const [fieldName, fieldDefinition] of fieldsToProcess) {
|
||||
const defaultResolver = get(fieldName);
|
||||
|
||||
const path = `${type}.${fieldName}`;
|
||||
const resolverConfig = getOr({}, path, resolversConfig);
|
||||
const path = `${type}.${fieldName}`;
|
||||
const resolverConfig = getOr({}, path, resolversConfig);
|
||||
|
||||
const { resolve: baseResolver = defaultResolver } = fieldDefinition;
|
||||
const { resolve: baseResolver = defaultResolver } = fieldDefinition;
|
||||
|
||||
// Parse & initialize the middlewares
|
||||
const middlewares = parseMiddlewares(resolverConfig, strapi);
|
||||
// Parse & initialize the middlewares
|
||||
const middlewares = parseMiddlewares(resolverConfig, strapi);
|
||||
|
||||
// Generate the policy middleware
|
||||
const policyMiddleware = createPoliciesMiddleware(resolverConfig, { strapi });
|
||||
// Generate the policy middleware
|
||||
const policyMiddleware = createPoliciesMiddleware(resolverConfig, { strapi });
|
||||
|
||||
// Add the policyMiddleware at the end of the middlewares collection
|
||||
middlewares.push(policyMiddleware);
|
||||
// Add the policyMiddleware at the end of the middlewares collection
|
||||
middlewares.push(policyMiddleware);
|
||||
|
||||
// Bind every middleware to the next one
|
||||
const boundMiddlewares = middlewares.map((middleware, index, collection) => {
|
||||
return (...args) =>
|
||||
middleware(
|
||||
// Make sure the last middleware in the list calls the baseResolver
|
||||
index >= collection.length - 1 ? baseResolver : boundMiddlewares[index + 1],
|
||||
...args
|
||||
);
|
||||
});
|
||||
// Bind every middleware to the next one
|
||||
const boundMiddlewares = middlewares.map((middleware, index, collection) => {
|
||||
return (...args) =>
|
||||
middleware(
|
||||
// Make sure the last middleware in the list calls the baseResolver
|
||||
index >= collection.length - 1 ? baseResolver : boundMiddlewares[index + 1],
|
||||
...args
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* GraphQL authorization flow
|
||||
* @param {object} context
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
const authorize = async ({ context }) => {
|
||||
const authConfig = get('auth', resolverConfig);
|
||||
const authContext = get('state.auth', context);
|
||||
/**
|
||||
* GraphQL authorization flow
|
||||
* @param {object} context
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
const authorize = async ({ context }) => {
|
||||
const authConfig = get('auth', resolverConfig);
|
||||
const authContext = get('state.auth', context);
|
||||
|
||||
const isValidType = ['Mutation', 'Query', 'Subscription'].includes(type);
|
||||
const hasConfig = !isNil(authConfig);
|
||||
const isValidType = ['Mutation', 'Query', 'Subscription'].includes(type);
|
||||
const hasConfig = !isNil(authConfig);
|
||||
|
||||
const isAuthDisabled = authConfig === false;
|
||||
const isAuthDisabled = authConfig === false;
|
||||
|
||||
if ((isValidType || hasConfig) && !isAuthDisabled) {
|
||||
try {
|
||||
await strapi.auth.verify(authContext, authConfig);
|
||||
} catch (error) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
if ((isValidType || hasConfig) && !isAuthDisabled) {
|
||||
try {
|
||||
await strapi.auth.verify(authContext, authConfig);
|
||||
} catch (error) {
|
||||
throw new ForbiddenError();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Base resolver wrapper that handles authorization, middlewares & policies
|
||||
* @param {object} parent
|
||||
* @param {object} args
|
||||
* @param {object} context
|
||||
* @param {object} info
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
fieldDefinition.resolve = async (parent, args, context, info) => {
|
||||
await authorize({ context });
|
||||
/**
|
||||
* Base resolver wrapper that handles authorization, middlewares & policies
|
||||
* @param {object} parent
|
||||
* @param {object} args
|
||||
* @param {object} context
|
||||
* @param {object} info
|
||||
* @return {Promise<any>}
|
||||
*/
|
||||
fieldDefinition.resolve = async (parent, args, context, info) => {
|
||||
await authorize({ context });
|
||||
|
||||
// Execute middlewares (including the policy middleware which will always be included)
|
||||
return first(boundMiddlewares).call(null, parent, args, context, info);
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
// Execute middlewares (including the policy middleware which will always be included)
|
||||
return first(boundMiddlewares).call(null, parent, args, context, info);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return schema;
|
||||
};
|
||||
|
||||
38
packages/plugins/graphql/server/services/utils/config.js
Normal file
38
packages/plugins/graphql/server/services/utils/config.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* GraphQL config helper with consistent defaults values
|
||||
*/
|
||||
module.exports = ({ strapi }) => {
|
||||
const { config: graphQLConfig } = strapi.plugin('graphql');
|
||||
|
||||
return {
|
||||
get shadowCRUD() {
|
||||
return graphQLConfig('shadowCRUD', true);
|
||||
},
|
||||
|
||||
get subscriptions() {
|
||||
return graphQLConfig('subscriptions', false);
|
||||
},
|
||||
|
||||
get endpoint() {
|
||||
return graphQLConfig('endpoint', '/graphql');
|
||||
},
|
||||
|
||||
get defaultLimit() {
|
||||
return graphQLConfig('defaultLimit');
|
||||
},
|
||||
|
||||
get maxLimit() {
|
||||
return graphQLConfig('maxLimit', -1);
|
||||
},
|
||||
|
||||
get depthLimit() {
|
||||
return graphQLConfig('depthLimit');
|
||||
},
|
||||
|
||||
get apolloServer() {
|
||||
return graphQLConfig('apolloServer', {});
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -3,9 +3,11 @@
|
||||
const mappers = require('./mappers');
|
||||
const attributes = require('./attributes');
|
||||
const naming = require('./naming');
|
||||
const config = require('./config');
|
||||
|
||||
module.exports = context => ({
|
||||
naming: naming(context),
|
||||
attributes: attributes(context),
|
||||
mappers: mappers(context),
|
||||
config: config(context),
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user