Files
kami-parse-server/src/GraphQL/loaders/parseClassMutations.js
Antoine Cormouls 59b0221fec GraphQL: Renaming Types/Inputs (#5883)
* Renaming GraphQL Types/Inputs

* Add Native Type to avoid collision

* Use pluralize for renaming

* Fixing tests

* Improve name collision management - tests passsing

* Renaming few more default types

* Rename file input

* Reverting fields types to not collide with the relay spec types
Improver users mutations

* Adding ArrayResult to the reserved list

* Fixing tests

* Add more unit tests to ParseGraphQLSchema

* Test transformClassNameToGraphQL

* Name collision tests
2019-08-15 14:23:41 -07:00

265 lines
8.3 KiB
JavaScript

import { GraphQLNonNull } from 'graphql';
import getFieldNames from 'graphql-list-fields';
import * as defaultGraphQLTypes from './defaultGraphQLTypes';
import { extractKeysAndInclude } from '../parseGraphQLUtils';
import * as objectsMutations from './objectsMutations';
import * as objectsQueries from './objectsQueries';
import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController';
import { transformClassNameToGraphQL } from '../transformers/className';
const getParseClassMutationConfig = function(
parseClassConfig: ?ParseGraphQLClassConfig
) {
return (parseClassConfig && parseClassConfig.mutation) || {};
};
const getOnlyRequiredFields = (
updatedFields,
selectedFieldsString,
includedFieldsString,
nativeObjectFields
) => {
const includedFields = includedFieldsString.split(',');
const selectedFields = selectedFieldsString.split(',');
const missingFields = selectedFields
.filter(
field =>
(!updatedFields[field] && !nativeObjectFields.includes(field)) ||
includedFields.includes(field)
)
.join(',');
if (!missingFields.length) {
return { needGet: false, keys: '' };
} else {
return { needGet: true, keys: missingFields };
}
};
const load = function(
parseGraphQLSchema,
parseClass,
parseClassConfig: ?ParseGraphQLClassConfig
) {
const className = parseClass.className;
const graphQLClassName = transformClassNameToGraphQL(className);
const {
create: isCreateEnabled = true,
update: isUpdateEnabled = true,
destroy: isDestroyEnabled = true,
} = getParseClassMutationConfig(parseClassConfig);
const {
classGraphQLCreateType,
classGraphQLUpdateType,
classGraphQLOutputType,
} = parseGraphQLSchema.parseClassTypes[className];
const transformTypes = (inputType: 'create' | 'update', fields) => {
if (fields) {
const classGraphQLCreateTypeFields =
isCreateEnabled && classGraphQLCreateType
? classGraphQLCreateType.getFields()
: null;
const classGraphQLUpdateTypeFields =
isUpdateEnabled && classGraphQLUpdateType
? classGraphQLUpdateType.getFields()
: null;
Object.keys(fields).forEach(field => {
let inputTypeField;
if (inputType === 'create' && classGraphQLCreateTypeFields) {
inputTypeField = classGraphQLCreateTypeFields[field];
} else if (classGraphQLUpdateTypeFields) {
inputTypeField = classGraphQLUpdateTypeFields[field];
}
if (inputTypeField) {
switch (inputTypeField.type) {
case defaultGraphQLTypes.GEO_POINT_INPUT:
fields[field].__type = 'GeoPoint';
break;
case defaultGraphQLTypes.POLYGON_INPUT:
fields[field] = {
__type: 'Polygon',
coordinates: fields[field].map(geoPoint => [
geoPoint.latitude,
geoPoint.longitude,
]),
};
break;
}
}
});
}
};
if (isCreateEnabled) {
const createGraphQLMutationName = `create${graphQLClassName}`;
parseGraphQLSchema.addGraphQLObjectMutation(createGraphQLMutationName, {
description: `The ${createGraphQLMutationName} mutation can be used to create a new object of the ${graphQLClassName} class.`,
args: {
fields: {
description: 'These are the fields used to create the object.',
type: classGraphQLCreateType || defaultGraphQLTypes.OBJECT,
},
},
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
let { fields } = args;
if (!fields) fields = {};
const { config, auth, info } = context;
transformTypes('create', fields);
const createdObject = await objectsMutations.createObject(
className,
fields,
config,
auth,
info
);
const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields);
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
fields,
keys,
include,
['objectId', 'createdAt', 'updatedAt']
);
let optimizedObject = {};
if (needGet) {
optimizedObject = await objectsQueries.getObject(
className,
createdObject.objectId,
requiredKeys,
include,
undefined,
undefined,
config,
auth,
info
);
}
return {
...createdObject,
updatedAt: createdObject.createdAt,
...fields,
...optimizedObject,
};
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
}
if (isUpdateEnabled) {
const updateGraphQLMutationName = `update${graphQLClassName}`;
parseGraphQLSchema.addGraphQLObjectMutation(updateGraphQLMutationName, {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: {
description: 'These are the fields used to update the object.',
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
},
},
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
const { objectId, fields } = args;
const { config, auth, info } = context;
transformTypes('update', fields);
const updatedObject = await objectsMutations.updateObject(
className,
objectId,
fields,
config,
auth,
info
);
const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields);
const { keys: requiredKeys, needGet } = getOnlyRequiredFields(
fields,
keys,
include,
['objectId', 'updatedAt']
);
let optimizedObject = {};
if (needGet) {
optimizedObject = await objectsQueries.getObject(
className,
objectId,
requiredKeys,
include,
undefined,
undefined,
config,
auth,
info
);
}
return { ...updatedObject, ...fields, ...optimizedObject };
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
}
if (isDestroyEnabled) {
const deleteGraphQLMutationName = `delete${graphQLClassName}`;
parseGraphQLSchema.addGraphQLObjectMutation(deleteGraphQLMutationName, {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT,
},
type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT
),
async resolve(_source, args, context, mutationInfo) {
try {
const { objectId } = args;
const { config, auth, info } = context;
const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields);
let optimizedObject = {};
const splitedKeys = keys.split(',');
if (splitedKeys.length > 1 || splitedKeys[0] !== 'objectId') {
optimizedObject = await objectsQueries.getObject(
className,
objectId,
keys,
include,
undefined,
undefined,
config,
auth,
info
);
}
await objectsMutations.deleteObject(
className,
objectId,
config,
auth,
info
);
return { objectId: objectId, ...optimizedObject };
} catch (e) {
parseGraphQLSchema.handleError(e);
}
},
});
}
};
export { load };