Relay Spec (#6089)
* Install graphql-relay * Add relayNodeInterface to ParseGraphQLSchema * Add support to global id * Add support to global id in other operations * Fix sort by glboal id * Fix where by global id * Introduce IdWhereInput * Add Relay object identification tests * Client mutation id on createFile mutation * Client mutation id on callCloudCode mutation * Client mutation id on signUp mutation * Client mutation id on logIn mutation * Client mutation id on logOut mutation * Client mutation id on createClass mutation * Client mutation id on updateClass mutation * Client mutation id on deleteClass mutation * Client mutation id on create object mutation * Improve Viewer type * Client mutation id on update object mutation * Client mutation id on delete object mutation * Introducing connections * Fix tests * Add pagination test * Fix file location * Fix postgres tests * Add comments * Tests to calculateSkipAndLimit
This commit is contained in:
committed by
GitHub
parent
67e3c33ffe
commit
a9066e20dc
@@ -1,4 +1,5 @@
|
||||
import { GraphQLNonNull } from 'graphql';
|
||||
import { fromGlobalId, mutationWithClientMutationId } from 'graphql-relay';
|
||||
import getFieldNames from 'graphql-list-fields';
|
||||
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
|
||||
import {
|
||||
@@ -17,13 +18,16 @@ const getOnlyRequiredFields = (
|
||||
includedFieldsString,
|
||||
nativeObjectFields
|
||||
) => {
|
||||
const includedFields = includedFieldsString.split(',');
|
||||
const selectedFields = selectedFieldsString.split(',');
|
||||
const includedFields = includedFieldsString
|
||||
? includedFieldsString.split(',')
|
||||
: [];
|
||||
const selectedFields = selectedFieldsString
|
||||
? selectedFieldsString.split(',')
|
||||
: [];
|
||||
const missingFields = selectedFields
|
||||
.filter(
|
||||
field =>
|
||||
!nativeObjectFields.includes(field) ||
|
||||
includedFields.includes(field)
|
||||
!nativeObjectFields.includes(field) || includedFields.includes(field)
|
||||
)
|
||||
.join(',');
|
||||
if (!missingFields.length) {
|
||||
@@ -40,6 +44,8 @@ const load = function(
|
||||
) {
|
||||
const className = parseClass.className;
|
||||
const graphQLClassName = transformClassNameToGraphQL(className);
|
||||
const getGraphQLQueryName =
|
||||
graphQLClassName.charAt(0).toLowerCase() + graphQLClassName.slice(1);
|
||||
|
||||
const {
|
||||
create: isCreateEnabled = true,
|
||||
@@ -55,18 +61,25 @@ const load = function(
|
||||
|
||||
if (isCreateEnabled) {
|
||||
const createGraphQLMutationName = `create${graphQLClassName}`;
|
||||
parseGraphQLSchema.addGraphQLMutation(createGraphQLMutationName, {
|
||||
const createGraphQLMutation = mutationWithClientMutationId({
|
||||
name: `Create${graphQLClassName}`,
|
||||
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
|
||||
args: {
|
||||
inputFields: {
|
||||
fields: {
|
||||
description: 'These are the fields used to create the object.',
|
||||
description:
|
||||
'These are the fields that will be used to create the new object.',
|
||||
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
|
||||
},
|
||||
},
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
async resolve(_source, args, context, mutationInfo) {
|
||||
outputFields: {
|
||||
[getGraphQLQueryName]: {
|
||||
description: 'This is the created object.',
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
try {
|
||||
let { fields } = args;
|
||||
if (!fields) fields = {};
|
||||
@@ -85,13 +98,15 @@ const load = function(
|
||||
auth,
|
||||
info
|
||||
);
|
||||
const selectedFields = getFieldNames(mutationInfo);
|
||||
const selectedFields = getFieldNames(mutationInfo)
|
||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
|
||||
fields,
|
||||
keys,
|
||||
include,
|
||||
['id', 'createdAt', 'updatedAt']
|
||||
['id', 'objectId', 'createdAt', 'updatedAt']
|
||||
);
|
||||
let optimizedObject = {};
|
||||
if (needGet) {
|
||||
@@ -108,37 +123,65 @@ const load = function(
|
||||
);
|
||||
}
|
||||
return {
|
||||
...createdObject,
|
||||
updatedAt: createdObject.createdAt,
|
||||
...parseFields,
|
||||
...optimizedObject,
|
||||
[getGraphQLQueryName]: {
|
||||
...createdObject,
|
||||
updatedAt: createdObject.createdAt,
|
||||
...parseFields,
|
||||
...optimizedObject,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
createGraphQLMutation.args.input.type.ofType
|
||||
) &&
|
||||
parseGraphQLSchema.addGraphQLType(createGraphQLMutation.type)
|
||||
) {
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
createGraphQLMutationName,
|
||||
createGraphQLMutation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isUpdateEnabled) {
|
||||
const updateGraphQLMutationName = `update${graphQLClassName}`;
|
||||
parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, {
|
||||
const updateGraphQLMutation = mutationWithClientMutationId({
|
||||
name: `Update${graphQLClassName}`,
|
||||
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
|
||||
args: {
|
||||
id: defaultGraphQLTypes.OBJECT_ID_ATT,
|
||||
inputFields: {
|
||||
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
||||
fields: {
|
||||
description: 'These are the fields used to update the object.',
|
||||
description:
|
||||
'These are the fields that will be used to update the object.',
|
||||
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
|
||||
},
|
||||
},
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
async resolve(_source, args, context, mutationInfo) {
|
||||
outputFields: {
|
||||
[getGraphQLQueryName]: {
|
||||
description: 'This is the updated object.',
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
try {
|
||||
const { id, fields } = args;
|
||||
let { id, fields } = args;
|
||||
if (!fields) fields = {};
|
||||
const { config, auth, info } = context;
|
||||
|
||||
const globalIdObject = fromGlobalId(id);
|
||||
|
||||
if (globalIdObject.type === className) {
|
||||
id = globalIdObject.id;
|
||||
}
|
||||
|
||||
const parseFields = await transformTypes('update', fields, {
|
||||
className,
|
||||
parseGraphQLSchema,
|
||||
@@ -154,15 +197,17 @@ const load = function(
|
||||
info
|
||||
);
|
||||
|
||||
const selectedFields = getFieldNames(mutationInfo);
|
||||
const selectedFields = getFieldNames(mutationInfo)
|
||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
|
||||
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
|
||||
fields,
|
||||
keys,
|
||||
include,
|
||||
['id', 'updatedAt']
|
||||
['id', 'objectId', 'updatedAt']
|
||||
);
|
||||
|
||||
let optimizedObject = {};
|
||||
if (needGet) {
|
||||
optimizedObject = await objectsQueries.getObject(
|
||||
@@ -178,38 +223,69 @@ const load = function(
|
||||
);
|
||||
}
|
||||
return {
|
||||
id,
|
||||
...updatedObject,
|
||||
...parseFields,
|
||||
...optimizedObject,
|
||||
[getGraphQLQueryName]: {
|
||||
objectId: id,
|
||||
...updatedObject,
|
||||
...parseFields,
|
||||
...optimizedObject,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
updateGraphQLMutation.args.input.type.ofType
|
||||
) &&
|
||||
parseGraphQLSchema.addGraphQLType(updateGraphQLMutation.type)
|
||||
) {
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
updateGraphQLMutationName,
|
||||
updateGraphQLMutation
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDestroyEnabled) {
|
||||
const deleteGraphQLMutationName = `delete${graphQLClassName}`;
|
||||
parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, {
|
||||
const deleteGraphQLMutation = mutationWithClientMutationId({
|
||||
name: `Delete${graphQLClassName}`,
|
||||
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
|
||||
args: {
|
||||
id: defaultGraphQLTypes.OBJECT_ID_ATT,
|
||||
inputFields: {
|
||||
id: defaultGraphQLTypes.GLOBAL_OR_OBJECT_ID_ATT,
|
||||
},
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
async resolve(_source, args, context, mutationInfo) {
|
||||
outputFields: {
|
||||
[getGraphQLQueryName]: {
|
||||
description: 'This is the deleted object.',
|
||||
type: new GraphQLNonNull(
|
||||
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
|
||||
),
|
||||
},
|
||||
},
|
||||
mutateAndGetPayload: async (args, context, mutationInfo) => {
|
||||
try {
|
||||
const { id } = args;
|
||||
let { id } = args;
|
||||
const { config, auth, info } = context;
|
||||
const selectedFields = getFieldNames(mutationInfo);
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
|
||||
const globalIdObject = fromGlobalId(id);
|
||||
|
||||
if (globalIdObject.type === className) {
|
||||
id = globalIdObject.id;
|
||||
}
|
||||
|
||||
const selectedFields = getFieldNames(mutationInfo)
|
||||
.filter(field => field.startsWith(`${getGraphQLQueryName}.`))
|
||||
.map(field => field.replace(`${getGraphQLQueryName}.`, ''));
|
||||
const { keys, include } = extractKeysAndInclude(selectedFields);
|
||||
let optimizedObject = {};
|
||||
const splitedKeys = keys.split(',');
|
||||
if (splitedKeys.length > 1 || splitedKeys[0] !== 'id') {
|
||||
if (
|
||||
keys &&
|
||||
keys.split(',').filter(key => !['id', 'objectId'].includes(key))
|
||||
.length > 0
|
||||
) {
|
||||
optimizedObject = await objectsQueries.getObject(
|
||||
className,
|
||||
id,
|
||||
@@ -229,12 +305,29 @@ const load = function(
|
||||
auth,
|
||||
info
|
||||
);
|
||||
return { id, ...optimizedObject };
|
||||
return {
|
||||
[getGraphQLQueryName]: {
|
||||
objectId: id,
|
||||
...optimizedObject,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
parseGraphQLSchema.handleError(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
parseGraphQLSchema.addGraphQLType(
|
||||
deleteGraphQLMutation.args.input.type.ofType
|
||||
) &&
|
||||
parseGraphQLSchema.addGraphQLType(deleteGraphQLMutation.type)
|
||||
) {
|
||||
parseGraphQLSchema.addGraphQLMutation(
|
||||
deleteGraphQLMutationName,
|
||||
deleteGraphQLMutation
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user