GraphQL DX: Relation/Pointer (#5946)

* Add a test on deep complex GraphQL Query

* Relation/Pointer new DX + deep nested mutations

* Fix lint

* Review

* Remove unnecessary code

* Fix objectId on update
This commit is contained in:
Antoine Cormouls
2019-08-21 23:55:34 +02:00
committed by Antonio Davi Macedo Coelho de Castro
parent 89e8868a85
commit 5b3a492965
7 changed files with 956 additions and 327 deletions

View File

@@ -1,21 +1,184 @@
const parseMap = {
_op: '__op',
};
import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes';
import * as objectsMutations from '../loaders/objectsMutations';
const transformMutationInputToParse = fields => {
if (!fields || typeof fields !== 'object') {
return;
const transformTypes = async (
inputType: 'create' | 'update',
fields,
{ className, parseGraphQLSchema, req }
) => {
const {
classGraphQLCreateType,
classGraphQLUpdateType,
config: { isCreateEnabled, isUpdateEnabled },
} = parseGraphQLSchema.parseClassTypes[className];
const parseClass = parseGraphQLSchema.parseClasses.find(
clazz => clazz.className === className
);
if (fields) {
const classGraphQLCreateTypeFields =
isCreateEnabled && classGraphQLCreateType
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields =
isUpdateEnabled && classGraphQLUpdateType
? classGraphQLUpdateType.getFields()
: null;
const promises = Object.keys(fields).map(async field => {
let inputTypeField;
if (inputType === 'create' && classGraphQLCreateTypeFields) {
inputTypeField = classGraphQLCreateTypeFields[field];
} else if (classGraphQLUpdateTypeFields) {
inputTypeField = classGraphQLUpdateTypeFields[field];
}
if (inputTypeField) {
switch (true) {
case inputTypeField.type === defaultGraphQLTypes.GEO_POINT_INPUT:
fields[field] = transformers.geoPoint(fields[field]);
break;
case inputTypeField.type === defaultGraphQLTypes.POLYGON_INPUT:
fields[field] = transformers.polygon(fields[field]);
break;
case parseClass.fields[field].type === 'Relation':
fields[field] = await transformers.relation(
parseClass.fields[field].targetClass,
field,
fields[field],
parseGraphQLSchema,
req
);
break;
case parseClass.fields[field].type === 'Pointer':
fields[field] = await transformers.pointer(
parseClass.fields[field].targetClass,
field,
fields[field],
parseGraphQLSchema,
req
);
break;
}
}
});
await Promise.all(promises);
}
Object.keys(fields).forEach(fieldName => {
const fieldValue = fields[fieldName];
if (parseMap[fieldName]) {
delete fields[fieldName];
fields[parseMap[fieldName]] = fieldValue;
}
if (typeof fieldValue === 'object') {
transformMutationInputToParse(fieldValue);
}
});
return fields;
};
export { transformMutationInputToParse };
const transformers = {
polygon: value => ({
__type: 'Polygon',
coordinates: value.map(geoPoint => [geoPoint.latitude, geoPoint.longitude]),
}),
geoPoint: value => ({
...value,
__type: 'GeoPoint',
}),
relation: async (
targetClass,
field,
value,
parseGraphQLSchema,
{ config, auth, info }
) => {
if (Object.keys(value) === 0)
throw new Error(
`You need to provide atleast one operation on the relation mutation of field ${field}`
);
const op = {
__op: 'Batch',
ops: [],
};
let nestedObjectsToAdd = [];
if (value.createAndAdd) {
nestedObjectsToAdd = (await Promise.all(
value.createAndAdd.map(async input => {
const parseFields = await transformTypes('create', input, {
className: targetClass,
parseGraphQLSchema,
req: { config, auth, info },
});
return objectsMutations.createObject(
targetClass,
parseFields,
config,
auth,
info
);
})
)).map(object => ({
__type: 'Pointer',
className: targetClass,
objectId: object.objectId,
}));
}
if (value.add || nestedObjectsToAdd.length > 0) {
if (!value.add) value.add = [];
value.add = value.add.map(input => ({
__type: 'Pointer',
className: targetClass,
objectId: input.objectId,
}));
op.ops.push({
__op: 'AddRelation',
objects: [...value.add, ...nestedObjectsToAdd],
});
}
if (value.remove) {
op.ops.push({
__op: 'RemoveRelation',
objects: value.remove.map(input => ({
__type: 'Pointer',
className: targetClass,
objectId: input.objectId,
})),
});
}
return op;
},
pointer: async (
targetClass,
field,
value,
parseGraphQLSchema,
{ config, auth, info }
) => {
if (Object.keys(value) > 1 || Object.keys(value) === 0)
throw new Error(
`You need to provide link OR createLink on the pointer mutation of field ${field}`
);
let nestedObjectToAdd;
if (value.createAndLink) {
const parseFields = await transformTypes('create', value.createAndLink, {
className: targetClass,
parseGraphQLSchema,
req: { config, auth, info },
});
nestedObjectToAdd = await objectsMutations.createObject(
targetClass,
parseFields,
config,
auth,
info
);
return {
__type: 'Pointer',
className: targetClass,
objectId: nestedObjectToAdd.objectId,
};
}
if (value.link && value.link.objectId) {
return {
__type: 'Pointer',
className: targetClass,
objectId: value.link.objectId,
};
}
},
};
export { transformTypes };