feat: Add GraphQL query cloudConfig to retrieve and mutation updateCloudConfig to update Cloud Config (#9947)

This commit is contained in:
Lucas Coratger
2025-12-03 19:55:30 +01:00
committed by GitHub
parent 6d670131a0
commit 3ca85cd4a6
6 changed files with 422 additions and 1 deletions

View File

@@ -49,7 +49,7 @@ const RESERVED_GRAPHQL_TYPE_NAMES = [
'DeleteClassPayload',
'PageInfo',
];
const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'class', 'classes'];
const RESERVED_GRAPHQL_QUERY_NAMES = ['health', 'viewer', 'class', 'classes', 'cloudConfig'];
const RESERVED_GRAPHQL_MUTATION_NAMES = [
'signUp',
'logIn',
@@ -59,6 +59,7 @@ const RESERVED_GRAPHQL_MUTATION_NAMES = [
'createClass',
'updateClass',
'deleteClass',
'updateCloudConfig',
];
class ParseGraphQLSchema {
@@ -118,6 +119,7 @@ class ParseGraphQLSchema {
this.functionNamesString = functionNamesString;
this.parseClassTypes = {};
this.viewerType = null;
this.cloudConfigType = null;
this.graphQLAutoSchema = null;
this.graphQLSchema = null;
this.graphQLTypes = [];

View File

@@ -0,0 +1,76 @@
import { GraphQLNonNull, GraphQLString, GraphQLBoolean } from 'graphql';
import { mutationWithClientMutationId } from 'graphql-relay';
import Parse from 'parse/node';
import { createSanitizedError } from '../../Error';
import GlobalConfigRouter from '../../Routers/GlobalConfigRouter';
const globalConfigRouter = new GlobalConfigRouter();
const updateCloudConfig = async (context, paramName, value, isMasterKeyOnly = false) => {
const { config, auth } = context;
if (!auth.isMaster) {
throw createSanitizedError(
Parse.Error.OPERATION_FORBIDDEN,
'Master Key is required to update GlobalConfig.'
);
}
await globalConfigRouter.updateGlobalConfig({
body: {
params: { [paramName]: value },
masterKeyOnly: { [paramName]: isMasterKeyOnly },
},
config,
auth,
context,
});
return { value, isMasterKeyOnly };
};
const load = parseGraphQLSchema => {
const updateCloudConfigMutation = mutationWithClientMutationId({
name: 'UpdateCloudConfig',
description: 'Updates the value of a specific parameter in GlobalConfig.',
inputFields: {
paramName: {
description: 'The name of the parameter to set.',
type: new GraphQLNonNull(GraphQLString),
},
value: {
description: 'The value to set for the parameter.',
type: new GraphQLNonNull(GraphQLString),
},
isMasterKeyOnly: {
description: 'Whether this parameter should only be accessible with master key.',
type: GraphQLBoolean,
defaultValue: false,
},
},
outputFields: {
cloudConfig: {
description: 'The updated config value.',
type: new GraphQLNonNull(parseGraphQLSchema.cloudConfigType),
},
},
mutateAndGetPayload: async (args, context) => {
try {
const { paramName, value, isMasterKeyOnly } = args;
const result = await updateCloudConfig(context, paramName, value, isMasterKeyOnly);
return {
cloudConfig: result,
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
parseGraphQLSchema.addGraphQLType(updateCloudConfigMutation.args.input.type.ofType, true, true);
parseGraphQLSchema.addGraphQLType(updateCloudConfigMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation('updateCloudConfig', updateCloudConfigMutation, true, true);
};
export { load, updateCloudConfig };

View File

@@ -0,0 +1,61 @@
import { GraphQLNonNull, GraphQLString, GraphQLBoolean, GraphQLObjectType } from 'graphql';
import Parse from 'parse/node';
import { createSanitizedError } from '../../Error';
const cloudConfig = async (context, paramName) => {
const { config, auth } = context;
if (!auth.isMaster) {
throw createSanitizedError(
Parse.Error.OPERATION_FORBIDDEN,
'Master Key is required to access GlobalConfig.'
);
}
const results = await config.database.find('_GlobalConfig', { objectId: '1' }, { limit: 1 });
if (results.length !== 1) {
return { value: null, isMasterKeyOnly: null };
}
const globalConfig = results[0];
const params = globalConfig.params || {};
const masterKeyOnly = globalConfig.masterKeyOnly || {};
if (params[paramName] !== undefined) {
return { value: params[paramName], isMasterKeyOnly: masterKeyOnly[paramName] ?? null };
}
return { value: null, isMasterKeyOnly: null };
};
const load = (parseGraphQLSchema) => {
if (!parseGraphQLSchema.cloudConfigType) {
const cloudConfigType = new GraphQLObjectType({
name: 'ConfigValue',
fields: {
value: { type: GraphQLString },
isMasterKeyOnly: { type: GraphQLBoolean },
},
});
parseGraphQLSchema.addGraphQLType(cloudConfigType, true, true);
parseGraphQLSchema.cloudConfigType = cloudConfigType;
}
parseGraphQLSchema.addGraphQLQuery('cloudConfig', {
description: 'Returns the value of a specific parameter from GlobalConfig.',
args: {
paramName: { type: new GraphQLNonNull(GraphQLString) },
},
type: new GraphQLNonNull(parseGraphQLSchema.cloudConfigType),
async resolve(_source, args, context) {
try {
return await cloudConfig(context, args.paramName);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
}, false, true);
};
export { load, cloudConfig };

View File

@@ -2,12 +2,14 @@ import * as filesMutations from './filesMutations';
import * as usersMutations from './usersMutations';
import * as functionsMutations from './functionsMutations';
import * as schemaMutations from './schemaMutations';
import * as configMutations from './configMutations';
const load = parseGraphQLSchema => {
filesMutations.load(parseGraphQLSchema);
usersMutations.load(parseGraphQLSchema);
functionsMutations.load(parseGraphQLSchema);
schemaMutations.load(parseGraphQLSchema);
configMutations.load(parseGraphQLSchema);
};
export { load };

View File

@@ -1,6 +1,7 @@
import { GraphQLNonNull, GraphQLBoolean } from 'graphql';
import * as usersQueries from './usersQueries';
import * as schemaQueries from './schemaQueries';
import * as configQueries from './configQueries';
const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLQuery(
@@ -16,6 +17,7 @@ const load = parseGraphQLSchema => {
usersQueries.load(parseGraphQLSchema);
schemaQueries.load(parseGraphQLSchema);
configQueries.load(parseGraphQLSchema);
};
export { load };