refactor(GraphQL): Rename objectId to id (#5985)

* refactor(GraphQL): Rename objectId to id

Renames `objectId` to `id` for the GraphQL API. Queries, mutations,
custom and generic types were updated.
Removes `RELATION_INPUT` and `POINTER_INPUT`. Now the user just need
to provide the ID of the object to link.

* fix: Column "id" not found on Postgres

* fix: Avoid deleting Parse class objectId

* fix: Undo objectId removal on mutations

* fix: Handle generic mutation id
This commit is contained in:
Douglas Muraoka
2019-08-30 20:23:46 -03:00
committed by Antonio Davi Macedo Coelho de Castro
parent 194f548464
commit b47d9fb17e
10 changed files with 496 additions and 546 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -395,16 +395,7 @@ const POLYGON_INPUT = new GraphQLList(new GraphQLNonNull(GEO_POINT_INPUT));
const POLYGON = new GraphQLList(new GraphQLNonNull(GEO_POINT)); const POLYGON = new GraphQLList(new GraphQLNonNull(GEO_POINT));
const RELATION_INPUT = new GraphQLInputObjectType({ const OBJECT_ID = new GraphQLNonNull(GraphQLID);
name: 'RelationInput',
description: 'Object involved into a relation',
fields: {
objectId: {
description: 'Id of the object involved.',
type: new GraphQLNonNull(GraphQLID),
},
},
});
const CLASS_NAME_ATT = { const CLASS_NAME_ATT = {
description: 'This is the class name of the object.', description: 'This is the class name of the object.',
@@ -418,7 +409,8 @@ const FIELDS_ATT = {
const OBJECT_ID_ATT = { const OBJECT_ID_ATT = {
description: 'This is the object id.', description: 'This is the object id.',
type: new GraphQLNonNull(GraphQLID), type: OBJECT_ID,
resolve: ({ objectId }) => objectId,
}; };
const CREATED_AT_ATT = { const CREATED_AT_ATT = {
@@ -441,7 +433,7 @@ const INPUT_FIELDS = {
}; };
const CREATE_RESULT_FIELDS = { const CREATE_RESULT_FIELDS = {
objectId: OBJECT_ID_ATT, id: OBJECT_ID_ATT,
createdAt: CREATED_AT_ATT, createdAt: CREATED_AT_ATT,
}; };
@@ -491,17 +483,6 @@ const INCLUDE_ATT = {
type: GraphQLString, type: GraphQLString,
}; };
const POINTER_INPUT = new GraphQLInputObjectType({
name: 'PointerInput',
description: 'Allow to link an object to another object',
fields: {
objectId: {
description: 'Id of the object involved.',
type: new GraphQLNonNull(GraphQLID),
},
},
});
const READ_PREFERENCE = new GraphQLEnumType({ const READ_PREFERENCE = new GraphQLEnumType({
name: 'ReadPreference', name: 'ReadPreference',
description: description:
@@ -1118,8 +1099,7 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLType(FIND_RESULT, true); parseGraphQLSchema.addGraphQLType(FIND_RESULT, true);
parseGraphQLSchema.addGraphQLType(SIGN_UP_RESULT, true); parseGraphQLSchema.addGraphQLType(SIGN_UP_RESULT, true);
parseGraphQLSchema.addGraphQLType(ELEMENT, true); parseGraphQLSchema.addGraphQLType(ELEMENT, true);
parseGraphQLSchema.addGraphQLType(RELATION_INPUT, true); parseGraphQLSchema.addGraphQLType(OBJECT_ID, true);
parseGraphQLSchema.addGraphQLType(POINTER_INPUT, true);
}; };
export { export {
@@ -1145,6 +1125,7 @@ export {
GEO_POINT, GEO_POINT,
POLYGON_INPUT, POLYGON_INPUT,
POLYGON, POLYGON,
OBJECT_ID,
CLASS_NAME_ATT, CLASS_NAME_ATT,
FIELDS_ATT, FIELDS_ATT,
OBJECT_ID_ATT, OBJECT_ID_ATT,
@@ -1206,8 +1187,6 @@ export {
SIGN_UP_RESULT, SIGN_UP_RESULT,
ARRAY_RESULT, ARRAY_RESULT,
ELEMENT, ELEMENT,
POINTER_INPUT,
RELATION_INPUT,
load, load,
loadArrayResult, loadArrayResult,
}; };

View File

@@ -54,7 +54,15 @@ const load = parseGraphQLSchema => {
const { className, fields } = args; const { className, fields } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
return await createObject(className, fields, config, auth, info); const object = await createObject(
className,
fields,
config,
auth,
info
);
return object;
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
@@ -71,23 +79,16 @@ const load = parseGraphQLSchema => {
'The update mutation can be used to update an object of a certain class.', 'The update mutation can be used to update an object of a certain class.',
args: { args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT, className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, id: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: defaultGraphQLTypes.FIELDS_ATT, fields: defaultGraphQLTypes.FIELDS_ATT,
}, },
type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT), type: new GraphQLNonNull(defaultGraphQLTypes.UPDATE_RESULT),
async resolve(_source, args, context) { async resolve(_source, args, context) {
try { try {
const { className, objectId, fields } = args; const { className, id, fields } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
return await updateObject( return await updateObject(className, id, fields, config, auth, info);
className,
objectId,
fields,
config,
auth,
info
);
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
@@ -104,15 +105,15 @@ const load = parseGraphQLSchema => {
'The delete mutation can be used to delete an object of a certain class.', 'The delete mutation can be used to delete an object of a certain class.',
args: { args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT, className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, id: defaultGraphQLTypes.OBJECT_ID_ATT,
}, },
type: new GraphQLNonNull(GraphQLBoolean), type: new GraphQLNonNull(GraphQLBoolean),
async resolve(_source, args, context) { async resolve(_source, args, context) {
try { try {
const { className, objectId } = args; const { className, id } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
return await deleteObject(className, objectId, config, auth, info); return await deleteObject(className, id, config, auth, info);
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }

View File

@@ -43,11 +43,11 @@ const getObject = async (
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.'); throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
} }
const object = response.results[0];
if (className === '_User') { if (className === '_User') {
delete response.results[0].sessionToken; delete object.sessionToken;
} }
return object;
return response.results[0];
}; };
const findObjects = async ( const findObjects = async (
@@ -70,7 +70,6 @@ const findObjects = async (
if (!where) { if (!where) {
where = {}; where = {};
} }
transformQueryInputToParse(where); transformQueryInputToParse(where);
const options = {}; const options = {};
@@ -136,7 +135,7 @@ const load = parseGraphQLSchema => {
'The get query can be used to get an object of a certain class by its objectId.', 'The get query can be used to get an object of a certain class by its objectId.',
args: { args: {
className: defaultGraphQLTypes.CLASS_NAME_ATT, className: defaultGraphQLTypes.CLASS_NAME_ATT,
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, id: defaultGraphQLTypes.OBJECT_ID_ATT,
keys: defaultGraphQLTypes.KEYS_ATT, keys: defaultGraphQLTypes.KEYS_ATT,
include: defaultGraphQLTypes.INCLUDE_ATT, include: defaultGraphQLTypes.INCLUDE_ATT,
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT, readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
@@ -147,7 +146,7 @@ const load = parseGraphQLSchema => {
try { try {
const { const {
className, className,
objectId, id,
keys, keys,
include, include,
readPreference, readPreference,
@@ -156,9 +155,9 @@ const load = parseGraphQLSchema => {
const { config, auth, info } = context; const { config, auth, info } = context;
return await getObject( const object = await getObject(
className, className,
objectId, id,
keys, keys,
include, include,
readPreference, readPreference,
@@ -167,6 +166,10 @@ const load = parseGraphQLSchema => {
auth, auth,
info info
); );
object.id = object.objectId;
delete object.objectId;
return object;
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }
@@ -222,7 +225,7 @@ const load = parseGraphQLSchema => {
const { config, auth, info } = context; const { config, auth, info } = context;
const selectedFields = getFieldNames(queryInfo); const selectedFields = getFieldNames(queryInfo);
return await findObjects( const objects = await findObjects(
className, className,
where, where,
order, order,
@@ -239,6 +242,11 @@ const load = parseGraphQLSchema => {
info, info,
selectedFields selectedFields
); );
objects.results.forEach(obj => {
obj.id = obj.objectId;
delete obj.objectId;
});
return objects;
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }

View File

@@ -91,7 +91,7 @@ const load = function(
fields, fields,
keys, keys,
include, include,
['objectId', 'createdAt', 'updatedAt'] ['id', 'createdAt', 'updatedAt']
); );
let optimizedObject = {}; let optimizedObject = {};
if (needGet) { if (needGet) {
@@ -125,7 +125,7 @@ const load = function(
parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, { parseGraphQLSchema.addGraphQLMutation(updateGraphQLMutationName, {
description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`, description: `The ${updateGraphQLMutationName} mutation can be used to update an object of the ${graphQLClassName} class.`,
args: { args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, id: defaultGraphQLTypes.OBJECT_ID_ATT,
fields: { fields: {
description: 'These are the fields used to update the object.', description: 'These are the fields used to update the object.',
type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT, type: classGraphQLUpdateType || defaultGraphQLTypes.OBJECT,
@@ -136,7 +136,7 @@ const load = function(
), ),
async resolve(_source, args, context, mutationInfo) { async resolve(_source, args, context, mutationInfo) {
try { try {
const { objectId, fields } = args; const { id, fields } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
const parseFields = await transformTypes('update', fields, { const parseFields = await transformTypes('update', fields, {
@@ -147,7 +147,7 @@ const load = function(
const updatedObject = await objectsMutations.updateObject( const updatedObject = await objectsMutations.updateObject(
className, className,
objectId, id,
parseFields, parseFields,
config, config,
auth, auth,
@@ -160,13 +160,13 @@ const load = function(
fields, fields,
keys, keys,
include, include,
['objectId', 'updatedAt'] ['id', 'updatedAt']
); );
let optimizedObject = {}; let optimizedObject = {};
if (needGet) { if (needGet) {
optimizedObject = await objectsQueries.getObject( optimizedObject = await objectsQueries.getObject(
className, className,
objectId, id,
requiredKeys, requiredKeys,
include, include,
undefined, undefined,
@@ -177,7 +177,7 @@ const load = function(
); );
} }
return { return {
objectId: objectId, id,
...updatedObject, ...updatedObject,
...fields, ...fields,
...optimizedObject, ...optimizedObject,
@@ -194,24 +194,24 @@ const load = function(
parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, { parseGraphQLSchema.addGraphQLMutation(deleteGraphQLMutationName, {
description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`, description: `The ${deleteGraphQLMutationName} mutation can be used to delete an object of the ${graphQLClassName} class.`,
args: { args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, id: defaultGraphQLTypes.OBJECT_ID_ATT,
}, },
type: new GraphQLNonNull( type: new GraphQLNonNull(
classGraphQLOutputType || defaultGraphQLTypes.OBJECT classGraphQLOutputType || defaultGraphQLTypes.OBJECT
), ),
async resolve(_source, args, context, mutationInfo) { async resolve(_source, args, context, mutationInfo) {
try { try {
const { objectId } = args; const { id } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
const selectedFields = getFieldNames(mutationInfo); const selectedFields = getFieldNames(mutationInfo);
const { keys, include } = extractKeysAndInclude(selectedFields); const { keys, include } = extractKeysAndInclude(selectedFields);
let optimizedObject = {}; let optimizedObject = {};
const splitedKeys = keys.split(','); const splitedKeys = keys.split(',');
if (splitedKeys.length > 1 || splitedKeys[0] !== 'objectId') { if (splitedKeys.length > 1 || splitedKeys[0] !== 'id') {
optimizedObject = await objectsQueries.getObject( optimizedObject = await objectsQueries.getObject(
className, className,
objectId, id,
keys, keys,
include, include,
undefined, undefined,
@@ -223,12 +223,12 @@ const load = function(
} }
await objectsMutations.deleteObject( await objectsMutations.deleteObject(
className, className,
objectId, id,
config, config,
auth, auth,
info info
); );
return { objectId: objectId, ...optimizedObject }; return { id, ...optimizedObject };
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
} }

View File

@@ -14,7 +14,7 @@ const getParseClassQueryConfig = function(
}; };
const getQuery = async (className, _source, args, context, queryInfo) => { const getQuery = async (className, _source, args, context, queryInfo) => {
const { objectId, readPreference, includeReadPreference } = args; const { id, readPreference, includeReadPreference } = args;
const { config, auth, info } = context; const { config, auth, info } = context;
const selectedFields = getFieldNames(queryInfo); const selectedFields = getFieldNames(queryInfo);
@@ -22,7 +22,7 @@ const getQuery = async (className, _source, args, context, queryInfo) => {
return await objectsQueries.getObject( return await objectsQueries.getObject(
className, className,
objectId, id,
keys, keys,
include, include,
readPreference, readPreference,
@@ -57,7 +57,7 @@ const load = function(
parseGraphQLSchema.addGraphQLQuery(getGraphQLQueryName, { parseGraphQLSchema.addGraphQLQuery(getGraphQLQueryName, {
description: `The ${getGraphQLQueryName} query can be used to get an object of the ${graphQLClassName} class by its id.`, description: `The ${getGraphQLQueryName} query can be used to get an object of the ${graphQLClassName} class by its id.`,
args: { args: {
objectId: defaultGraphQLTypes.OBJECT_ID_ATT, id: defaultGraphQLTypes.OBJECT_ID_ATT,
readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT, readPreference: defaultGraphQLTypes.READ_PREFERENCE_ATT,
includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT, includeReadPreference: defaultGraphQLTypes.INCLUDE_READ_PREFERENCE_ATT,
}, },

View File

@@ -1,5 +1,6 @@
import { import {
Kind, Kind,
GraphQLID,
GraphQLObjectType, GraphQLObjectType,
GraphQLString, GraphQLString,
GraphQLFloat, GraphQLFloat,
@@ -165,7 +166,9 @@ const getInputFieldsAndConstraints = function(
parseClass, parseClass,
parseClassConfig: ?ParseGraphQLClassConfig parseClassConfig: ?ParseGraphQLClassConfig
) { ) {
const classFields = Object.keys(parseClass.fields); const classFields = Object.keys(parseClass.fields)
.filter(field => field !== 'objectId')
.concat('id');
const { const {
inputFields: allowedInputFields, inputFields: allowedInputFields,
outputFields: allowedOutputFields, outputFields: allowedOutputFields,
@@ -227,7 +230,7 @@ const getInputFieldsAndConstraints = function(
// must have at least 1 order field // must have at least 1 order field
// otherwise the FindArgs Input Type will throw. // otherwise the FindArgs Input Type will throw.
classSortFields.push({ classSortFields.push({
field: 'objectId', field: 'id',
asc: true, asc: true,
desc: true, desc: true,
}); });
@@ -421,7 +424,7 @@ const load = (
const fields = { const fields = {
link: { link: {
description: `Link an existing object from ${graphQLClassName} class.`, description: `Link an existing object from ${graphQLClassName} class.`,
type: defaultGraphQLTypes.POINTER_INPUT, type: GraphQLID,
}, },
}; };
if (isCreateEnabled) { if (isCreateEnabled) {
@@ -445,15 +448,11 @@ const load = (
const fields = { const fields = {
add: { add: {
description: `Add an existing object from the ${graphQLClassName} class into the relation.`, description: `Add an existing object from the ${graphQLClassName} class into the relation.`,
type: new GraphQLList( type: new GraphQLList(defaultGraphQLTypes.OBJECT_ID),
new GraphQLNonNull(defaultGraphQLTypes.RELATION_INPUT)
),
}, },
remove: { remove: {
description: `Remove an existing object from the ${graphQLClassName} class out of the relation.`, description: `Remove an existing object from the ${graphQLClassName} class out of the relation.`,
type: new GraphQLList( type: new GraphQLList(defaultGraphQLTypes.OBJECT_ID),
new GraphQLNonNull(defaultGraphQLTypes.RELATION_INPUT)
),
}, },
}; };
if (isCreateEnabled) { if (isCreateEnabled) {
@@ -503,9 +502,10 @@ const load = (
description: `The ${classGraphQLConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`, description: `The ${classGraphQLConstraintsTypeName} input type is used in operations that involve filtering objects of ${graphQLClassName} class.`,
fields: () => ({ fields: () => ({
...classConstraintFields.reduce((fields, field) => { ...classConstraintFields.reduce((fields, field) => {
const parseField = field === 'id' ? 'objectId' : field;
const type = mapConstraintType( const type = mapConstraintType(
parseClass.fields[field].type, parseClass.fields[parseField].type,
parseClass.fields[field].targetClass, parseClass.fields[parseField].targetClass,
parseGraphQLSchema.parseClassTypes parseGraphQLSchema.parseClassTypes
); );
if (type) { if (type) {

View File

@@ -17,9 +17,17 @@ export const extractKeysAndInclude = selectedFields => {
selectedFields = selectedFields.filter( selectedFields = selectedFields.filter(
field => !field.includes('__typename') field => !field.includes('__typename')
); );
// Handles "id" field for both current and included objects
selectedFields = selectedFields.map(field => {
if (field === 'id') return 'objectId';
return field.endsWith('.id')
? `${field.substring(0, field.lastIndexOf('.id'))}.objectId`
: field;
});
let keys = undefined; let keys = undefined;
let include = undefined; let include = undefined;
if (selectedFields && selectedFields.length > 0) { if (selectedFields.length > 0) {
keys = selectedFields.join(','); keys = selectedFields.join(',');
include = selectedFields include = selectedFields
.reduce((fields, field) => { .reduce((fields, field) => {

View File

@@ -119,7 +119,7 @@ const transformers = {
value.add = value.add.map(input => ({ value.add = value.add.map(input => ({
__type: 'Pointer', __type: 'Pointer',
className: targetClass, className: targetClass,
objectId: input.objectId, objectId: input,
})); }));
op.ops.push({ op.ops.push({
__op: 'AddRelation', __op: 'AddRelation',
@@ -133,7 +133,7 @@ const transformers = {
objects: value.remove.map(input => ({ objects: value.remove.map(input => ({
__type: 'Pointer', __type: 'Pointer',
className: targetClass, className: targetClass,
objectId: input.objectId, objectId: input,
})), })),
}); });
} }
@@ -171,11 +171,11 @@ const transformers = {
objectId: nestedObjectToAdd.objectId, objectId: nestedObjectToAdd.objectId,
}; };
} }
if (value.link && value.link.objectId) { if (value.link) {
return { return {
__type: 'Pointer', __type: 'Pointer',
className: targetClass, className: targetClass,
objectId: value.link.objectId, objectId: value.link,
}; };
} }
}, },

View File

@@ -1,4 +1,5 @@
const parseMap = { const parseMap = {
id: 'objectId',
_or: '$or', _or: '$or',
_and: '$and', _and: '$and',
_nor: '$nor', _nor: '$nor',