refactor(GraphQL): Pointer constraint input type as ID (#6020)

* refactor(GraphQL): Pointer constraint input type as ID

Redefines the Pointer constraint input type from a custom scalar to
a simple ID.

* fix: PR review requested changes
This commit is contained in:
Douglas Muraoka
2019-09-04 19:46:18 -03:00
committed by Antonio Davi Macedo Coelho de Castro
parent 34f1bf384d
commit f9b77c1bc7
5 changed files with 68 additions and 109 deletions

View File

@@ -3384,11 +3384,7 @@ describe('ParseGraphQLServer', () => {
OR: [ OR: [
{ {
pointerToUser: { pointerToUser: {
equalTo: { equalTo: user5.id,
__type: 'Pointer',
className: '_User',
objectId: user5.id,
},
}, },
}, },
{ {
@@ -3413,6 +3409,40 @@ describe('ParseGraphQLServer', () => {
).toEqual(['someValue1', 'someValue3']); ).toEqual(['someValue1', 'someValue3']);
}); });
it('should support in pointer operator using class specific query', async () => {
await prepareData();
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
const result = await apolloClient.query({
query: gql`
query FindSomeObjects($where: GraphQLClassWhereInput) {
graphQLClasses(where: $where) {
results {
someField
}
}
}
`,
variables: {
where: {
pointerToUser: {
in: [user5.id],
},
},
},
context: {
headers: {
'X-Parse-Master-Key': 'test',
},
},
});
const { results } = result.data.graphQLClasses;
expect(results.length).toBe(1);
expect(results[0].someField).toEqual('someValue3');
});
it('should support OR operation', async () => { it('should support OR operation', async () => {
await prepareData(); await prepareData();
@@ -3558,11 +3588,7 @@ describe('ParseGraphQLServer', () => {
OR: [ OR: [
{ {
pointerToUser: { pointerToUser: {
equalTo: { equalTo: user5.id,
__type: 'Pointer',
className: '_User',
objectId: user5.id,
},
}, },
}, },
{ {
@@ -3614,11 +3640,7 @@ describe('ParseGraphQLServer', () => {
OR: [ OR: [
{ {
pointerToUser: { pointerToUser: {
equalTo: { equalTo: user5.id,
__type: 'Pointer',
className: '_User',
objectId: user5.id,
},
}, },
}, },
{ {

View File

@@ -62,12 +62,13 @@ const findObjects = async (
config, config,
auth, auth,
info, info,
selectedFields selectedFields,
fields
) => { ) => {
if (!where) { if (!where) {
where = {}; where = {};
} }
transformQueryInputToParse(where); transformQueryInputToParse(where, fields);
const options = {}; const options = {};

View File

@@ -120,7 +120,8 @@ const load = function(
config, config,
auth, auth,
info, info,
selectedFields.map(field => field.split('.', 1)[0]) selectedFields.map(field => field.split('.', 1)[0]),
parseClass.fields
); );
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);

View File

@@ -1,12 +1,10 @@
import { import {
Kind,
GraphQLID, GraphQLID,
GraphQLObjectType, GraphQLObjectType,
GraphQLString, GraphQLString,
GraphQLList, GraphQLList,
GraphQLInputObjectType, GraphQLInputObjectType,
GraphQLNonNull, GraphQLNonNull,
GraphQLScalarType,
GraphQLEnumType, GraphQLEnumType,
} from 'graphql'; } from 'graphql';
import getFieldNames from 'graphql-list-fields'; import getFieldNames from 'graphql-list-fields';
@@ -138,86 +136,6 @@ const load = (
update: isUpdateEnabled = true, update: isUpdateEnabled = true,
} = getParseClassMutationConfig(parseClassConfig); } = getParseClassMutationConfig(parseClassConfig);
const classGraphQLScalarTypeName = `${graphQLClassName}Pointer`;
const parseScalarValue = value => {
if (typeof value === 'string') {
return {
__type: 'Pointer',
className: className,
objectId: value,
};
} else if (
typeof value === 'object' &&
value.__type === 'Pointer' &&
value.className === className &&
typeof value.objectId === 'string'
) {
return { ...value, className };
}
throw new defaultGraphQLTypes.TypeValidationError(
value,
classGraphQLScalarTypeName
);
};
let classGraphQLScalarType = new GraphQLScalarType({
name: classGraphQLScalarTypeName,
description: `The ${classGraphQLScalarTypeName} is used in operations that involve ${graphQLClassName} pointers.`,
parseValue: parseScalarValue,
serialize(value) {
if (typeof value === 'string') {
return value;
} else if (
typeof value === 'object' &&
value.__type === 'Pointer' &&
value.className === className &&
typeof value.objectId === 'string'
) {
return value.objectId;
}
throw new defaultGraphQLTypes.TypeValidationError(
value,
classGraphQLScalarTypeName
);
},
parseLiteral(ast) {
if (ast.kind === Kind.STRING) {
return parseScalarValue(ast.value);
} else if (ast.kind === Kind.OBJECT) {
const __type = ast.fields.find(field => field.name.value === '__type');
const className = ast.fields.find(
field => field.name.value === 'className'
);
const objectId = ast.fields.find(
field => field.name.value === 'objectId'
);
if (
__type &&
__type.value &&
className &&
className.value &&
objectId &&
objectId.value
) {
return parseScalarValue({
__type: __type.value.value,
className: className.value.value,
objectId: objectId.value.value,
});
}
}
throw new defaultGraphQLTypes.TypeValidationError(
ast.kind,
classGraphQLScalarTypeName
);
},
});
classGraphQLScalarType =
parseGraphQLSchema.addGraphQLType(classGraphQLScalarType) ||
defaultGraphQLTypes.OBJECT;
const classGraphQLCreateTypeName = `Create${graphQLClassName}FieldsInput`; const classGraphQLCreateTypeName = `Create${graphQLClassName}FieldsInput`;
let classGraphQLCreateType = new GraphQLInputObjectType({ let classGraphQLCreateType = new GraphQLInputObjectType({
name: classGraphQLCreateTypeName, name: classGraphQLCreateTypeName,
@@ -341,10 +259,10 @@ const load = (
name: classGraphQLConstraintTypeName, name: classGraphQLConstraintTypeName,
description: `The ${classGraphQLConstraintTypeName} input type is used in operations that involve filtering objects by a pointer field to ${graphQLClassName} class.`, description: `The ${classGraphQLConstraintTypeName} input type is used in operations that involve filtering objects by a pointer field to ${graphQLClassName} class.`,
fields: { fields: {
equalTo: defaultGraphQLTypes.equalTo(classGraphQLScalarType), equalTo: defaultGraphQLTypes.equalTo(GraphQLID),
notEqualTo: defaultGraphQLTypes.notEqualTo(classGraphQLScalarType), notEqualTo: defaultGraphQLTypes.notEqualTo(GraphQLID),
in: defaultGraphQLTypes.inOp(classGraphQLScalarType), in: defaultGraphQLTypes.inOp(defaultGraphQLTypes.OBJECT_ID),
notIn: defaultGraphQLTypes.notIn(classGraphQLScalarType), notIn: defaultGraphQLTypes.notIn(defaultGraphQLTypes.OBJECT_ID),
exists: defaultGraphQLTypes.exists, exists: defaultGraphQLTypes.exists,
inQueryKey: defaultGraphQLTypes.inQueryKey, inQueryKey: defaultGraphQLTypes.inQueryKey,
notInQueryKey: defaultGraphQLTypes.notInQueryKey, notInQueryKey: defaultGraphQLTypes.notInQueryKey,
@@ -519,7 +437,8 @@ const load = (
config, config,
auth, auth,
info, info,
selectedFields.map(field => field.split('.', 1)[0]) selectedFields.map(field => field.split('.', 1)[0]),
parseClass.fields
); );
} catch (e) { } catch (e) {
parseGraphQLSchema.handleError(e); parseGraphQLSchema.handleError(e);
@@ -615,7 +534,6 @@ const load = (
parseGraphQLSchema.parseClassTypes[className] = { parseGraphQLSchema.parseClassTypes[className] = {
classGraphQLPointerType, classGraphQLPointerType,
classGraphQLRelationType, classGraphQLRelationType,
classGraphQLScalarType,
classGraphQLCreateType, classGraphQLCreateType,
classGraphQLUpdateType, classGraphQLUpdateType,
classGraphQLConstraintType, classGraphQLConstraintType,

View File

@@ -45,6 +45,7 @@ const parseConstraintMap = {
const transformQueryConstraintInputToParse = ( const transformQueryConstraintInputToParse = (
constraints, constraints,
fields,
parentFieldName, parentFieldName,
parentConstraints parentConstraints
) => { ) => {
@@ -92,6 +93,21 @@ const transformQueryConstraintInputToParse = (
delete constraints[fieldName]; delete constraints[fieldName];
fieldName = parseConstraintMap[fieldName]; fieldName = parseConstraintMap[fieldName];
constraints[fieldName] = fieldValue; constraints[fieldName] = fieldValue;
// If parent field type is Pointer, changes constraint value to format expected
// by Parse.
if (
fields[parentFieldName] &&
fields[parentFieldName].type === 'Pointer' &&
typeof fieldValue === 'string'
) {
const { targetClass } = fields[parentFieldName];
constraints[fieldName] = {
__type: 'Pointer',
className: targetClass,
objectId: fieldValue,
};
}
} }
switch (fieldName) { switch (fieldName) {
case '$point': case '$point':
@@ -151,6 +167,7 @@ const transformQueryConstraintInputToParse = (
} else { } else {
transformQueryConstraintInputToParse( transformQueryConstraintInputToParse(
fieldValue, fieldValue,
fields,
fieldName, fieldName,
constraints constraints
); );
@@ -159,7 +176,7 @@ const transformQueryConstraintInputToParse = (
}); });
}; };
const transformQueryInputToParse = constraints => { const transformQueryInputToParse = (constraints, fields) => {
if (!constraints || typeof constraints !== 'object') { if (!constraints || typeof constraints !== 'object') {
return; return;
} }
@@ -174,14 +191,14 @@ const transformQueryInputToParse = constraints => {
if (fieldName !== 'objectId') { if (fieldName !== 'objectId') {
fieldValue.forEach(fieldValueItem => { fieldValue.forEach(fieldValueItem => {
transformQueryInputToParse(fieldValueItem); transformQueryInputToParse(fieldValueItem, fields);
}); });
return; return;
} }
} }
if (typeof fieldValue === 'object') { if (typeof fieldValue === 'object') {
transformQueryConstraintInputToParse(fieldValue, fieldName, constraints); transformQueryConstraintInputToParse(fieldValue, fields, fieldName, constraints);
} }
}); });
}; };