Skip to content

Commit

Permalink
refactor(GraphQL): Pointer constraint input type as ID (#6020)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
douglasmuraoka authored and davimacedo committed Sep 4, 2019
1 parent 34f1bf3 commit f9b77c1
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 109 deletions.
52 changes: 37 additions & 15 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3384,11 +3384,7 @@ describe('ParseGraphQLServer', () => {
OR: [
{
pointerToUser: {
equalTo: {
__type: 'Pointer',
className: '_User',
objectId: user5.id,
},
equalTo: user5.id,
},
},
{
Expand All @@ -3413,6 +3409,40 @@ describe('ParseGraphQLServer', () => {
).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 () => {
await prepareData();

Expand Down Expand Up @@ -3558,11 +3588,7 @@ describe('ParseGraphQLServer', () => {
OR: [
{
pointerToUser: {
equalTo: {
__type: 'Pointer',
className: '_User',
objectId: user5.id,
},
equalTo: user5.id,
},
},
{
Expand Down Expand Up @@ -3614,11 +3640,7 @@ describe('ParseGraphQLServer', () => {
OR: [
{
pointerToUser: {
equalTo: {
__type: 'Pointer',
className: '_User',
objectId: user5.id,
},
equalTo: user5.id,
},
},
{
Expand Down
5 changes: 3 additions & 2 deletions src/GraphQL/helpers/objectsQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ const findObjects = async (
config,
auth,
info,
selectedFields
selectedFields,
fields
) => {
if (!where) {
where = {};
}
transformQueryInputToParse(where);
transformQueryInputToParse(where, fields);

const options = {};

Expand Down
3 changes: 2 additions & 1 deletion src/GraphQL/loaders/parseClassQueries.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ const load = function(
config,
auth,
info,
selectedFields.map(field => field.split('.', 1)[0])
selectedFields.map(field => field.split('.', 1)[0]),
parseClass.fields
);
} catch (e) {
parseGraphQLSchema.handleError(e);
Expand Down
94 changes: 6 additions & 88 deletions src/GraphQL/loaders/parseClassTypes.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {
Kind,
GraphQLID,
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLInputObjectType,
GraphQLNonNull,
GraphQLScalarType,
GraphQLEnumType,
} from 'graphql';
import getFieldNames from 'graphql-list-fields';
Expand Down Expand Up @@ -138,86 +136,6 @@ const load = (
update: isUpdateEnabled = true,
} = 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`;
let classGraphQLCreateType = new GraphQLInputObjectType({
name: classGraphQLCreateTypeName,
Expand Down Expand Up @@ -341,10 +259,10 @@ const load = (
name: classGraphQLConstraintTypeName,
description: `The ${classGraphQLConstraintTypeName} input type is used in operations that involve filtering objects by a pointer field to ${graphQLClassName} class.`,
fields: {
equalTo: defaultGraphQLTypes.equalTo(classGraphQLScalarType),
notEqualTo: defaultGraphQLTypes.notEqualTo(classGraphQLScalarType),
in: defaultGraphQLTypes.inOp(classGraphQLScalarType),
notIn: defaultGraphQLTypes.notIn(classGraphQLScalarType),
equalTo: defaultGraphQLTypes.equalTo(GraphQLID),
notEqualTo: defaultGraphQLTypes.notEqualTo(GraphQLID),
in: defaultGraphQLTypes.inOp(defaultGraphQLTypes.OBJECT_ID),
notIn: defaultGraphQLTypes.notIn(defaultGraphQLTypes.OBJECT_ID),
exists: defaultGraphQLTypes.exists,
inQueryKey: defaultGraphQLTypes.inQueryKey,
notInQueryKey: defaultGraphQLTypes.notInQueryKey,
Expand Down Expand Up @@ -519,7 +437,8 @@ const load = (
config,
auth,
info,
selectedFields.map(field => field.split('.', 1)[0])
selectedFields.map(field => field.split('.', 1)[0]),
parseClass.fields
);
} catch (e) {
parseGraphQLSchema.handleError(e);
Expand Down Expand Up @@ -615,7 +534,6 @@ const load = (
parseGraphQLSchema.parseClassTypes[className] = {
classGraphQLPointerType,
classGraphQLRelationType,
classGraphQLScalarType,
classGraphQLCreateType,
classGraphQLUpdateType,
classGraphQLConstraintType,
Expand Down
23 changes: 20 additions & 3 deletions src/GraphQL/transformers/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const parseConstraintMap = {

const transformQueryConstraintInputToParse = (
constraints,
fields,
parentFieldName,
parentConstraints
) => {
Expand Down Expand Up @@ -92,6 +93,21 @@ const transformQueryConstraintInputToParse = (
delete constraints[fieldName];
fieldName = parseConstraintMap[fieldName];
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) {
case '$point':
Expand Down Expand Up @@ -151,6 +167,7 @@ const transformQueryConstraintInputToParse = (
} else {
transformQueryConstraintInputToParse(
fieldValue,
fields,
fieldName,
constraints
);
Expand All @@ -159,7 +176,7 @@ const transformQueryConstraintInputToParse = (
});
};

const transformQueryInputToParse = constraints => {
const transformQueryInputToParse = (constraints, fields) => {
if (!constraints || typeof constraints !== 'object') {
return;
}
Expand All @@ -174,14 +191,14 @@ const transformQueryInputToParse = constraints => {

if (fieldName !== 'objectId') {
fieldValue.forEach(fieldValueItem => {
transformQueryInputToParse(fieldValueItem);
transformQueryInputToParse(fieldValueItem, fields);
});
return;
}
}

if (typeof fieldValue === 'object') {
transformQueryConstraintInputToParse(fieldValue, fieldName, constraints);
transformQueryConstraintInputToParse(fieldValue, fields, fieldName, constraints);
}
});
};
Expand Down

0 comments on commit f9b77c1

Please sign in to comment.