From 65d28b988c6aadff9ccaa8720c43b0bc2171d56d Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Mon, 12 Aug 2019 17:41:38 +0200 Subject: [PATCH 1/6] Inline Fragment Spec --- spec/ParseGraphQLServer.spec.js | 100 ++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index cbd16300d9..754cef2a23 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -539,6 +539,19 @@ describe('ParseGraphQLServer', () => { expect(dateType.kind).toEqual('SCALAR'); }); + it('should have ArrayResult type', async () => { + const arrayResultType = (await apolloClient.query({ + query: gql` + query ArrayResultType { + __type(name: "ArrayResult") { + kind + } + } + `, + })).data['__type']; + expect(arrayResultType.kind).toEqual('UNION'); + }); + it('should have File object type', async () => { const fileType = (await apolloClient.query({ query: gql` @@ -746,6 +759,25 @@ describe('ParseGraphQLServer', () => { ).toBeTruthy(JSON.stringify(schemaTypes)); }); + it('should ArrayResult contains all types', async () => { + const objectType = (await apolloClient.query({ + query: gql` + query ObjectType { + __type(name: "ArrayResult") { + kind + possibleTypes { + name + } + } + } + `, + })).data['__type']; + const possibleTypes = objectType.possibleTypes.map(o => o.name); + expect(possibleTypes).toContain('_UserClass'); + expect(possibleTypes).toContain('_RoleClass'); + expect(possibleTypes).toContain('Any'); + }); + it('should update schema when it changes', async () => { const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.updateClass('_User', { @@ -1661,6 +1693,74 @@ describe('ParseGraphQLServer', () => { expect(new Date(result.updatedAt)).toEqual(obj.updatedAt); }); + it('should return child objects in array fields', async () => { + const obj1 = new Parse.Object('Customer'); + const obj2 = new Parse.Object('SomeClass'); + const obj3 = new Parse.Object('Customer'); + + obj1.set('someCustomerField', 'imCustomerOne'); + const arrayField = [42.42, 42, 'string', true]; + obj1.set('arrayField', arrayField); + await obj1.save(); + + obj2.set('someClassField', 'imSomeClassTwo'); + await obj3.save(); + + //const obj3Relation = obj3.relation('manyRelations') + obj3.set('manyRelations', [obj1, obj2]); + await obj3.save(); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + const result = (await apolloClient.query({ + query: gql` + query GetCustomer($objectId: ID!) { + objects { + getCustomer(objectId: $objectId) { + objectId + manyRelations { + ...on CustomerClass { + objectId + someCustomerField + arrayField { + ...on Any + } + } + ...on SomeClassClass { + objectId + someClassField + } + } + createdAt + updatedAt + } + } + } + `, + variables: { + objectId: obj2.id, + }, + })).data.objects.getCustomer; + + expect(result.objectId).toEqual(obj2.id); + expect(result.manyRelations.length).toEqual(2); + + const customerSubObject = result.manyRelations.find( + o => o.objectId === obj1.id + ); + const someClassSubObject = result.manyRelations.find( + o => o.objectId === obj2.id + ); + + expect(customerSubObject).toBeDefined(); + expect(someClassSubObject).toBeDefined(); + expect(customerSubObject.someCustomerField).toEqual( + 'imCustomerOne' + ); + expect(customerSubObject.arrayField).toEqual(arrayField); + expect(someClassSubObject.someClassField).toEqual('imSomeClassTwo'); + }); + it('should respect level permissions', async () => { await prepareData(); From f56d6bc1ef0771654fa9317e1403c6d8c0dc627e Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Tue, 13 Aug 2019 03:02:30 +0200 Subject: [PATCH 2/6] Inline Fragment on Arrays --- spec/ParseGraphQLServer.spec.js | 25 ++++++---- src/GraphQL/ParseGraphQLSchema.js | 2 +- src/GraphQL/loaders/defaultGraphQLTypes.js | 56 ++++++++++++++++++++++ src/GraphQL/loaders/parseClassMutations.js | 14 ++---- src/GraphQL/loaders/parseClassQueries.js | 46 +++++++++--------- src/GraphQL/loaders/parseClassTypes.js | 51 +++++++++----------- src/GraphQL/parseGraphQLUtils.js | 27 +++++++++++ 7 files changed, 152 insertions(+), 69 deletions(-) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 754cef2a23..dff8f9e722 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -775,7 +775,7 @@ describe('ParseGraphQLServer', () => { const possibleTypes = objectType.possibleTypes.map(o => o.name); expect(possibleTypes).toContain('_UserClass'); expect(possibleTypes).toContain('_RoleClass'); - expect(possibleTypes).toContain('Any'); + expect(possibleTypes).toContain('Element'); }); it('should update schema when it changes', async () => { @@ -1719,14 +1719,16 @@ describe('ParseGraphQLServer', () => { getCustomer(objectId: $objectId) { objectId manyRelations { - ...on CustomerClass { + ... on CustomerClass { objectId someCustomerField arrayField { - ...on Any + ... on Element { + value + } } } - ...on SomeClassClass { + ... on SomeClassClass { objectId someClassField } @@ -1738,11 +1740,11 @@ describe('ParseGraphQLServer', () => { } `, variables: { - objectId: obj2.id, + objectId: obj3.id, }, })).data.objects.getCustomer; - expect(result.objectId).toEqual(obj2.id); + expect(result.objectId).toEqual(obj3.id); expect(result.manyRelations.length).toEqual(2); const customerSubObject = result.manyRelations.find( @@ -1757,7 +1759,10 @@ describe('ParseGraphQLServer', () => { expect(customerSubObject.someCustomerField).toEqual( 'imCustomerOne' ); - expect(customerSubObject.arrayField).toEqual(arrayField); + const formatedArrayField = customerSubObject.arrayField.map( + elem => elem.value + ); + expect(formatedArrayField).toEqual(arrayField); expect(someClassSubObject.someClassField).toEqual('imSomeClassTwo'); }); @@ -5701,7 +5706,11 @@ describe('ParseGraphQLServer', () => { findSomeClass(where: { someField: { _exists: true } }) { results { objectId - someField + someField { + ... on Element { + value + } + } } } } diff --git a/src/GraphQL/ParseGraphQLSchema.js b/src/GraphQL/ParseGraphQLSchema.js index 261045fe81..a0811b2fe1 100644 --- a/src/GraphQL/ParseGraphQLSchema.js +++ b/src/GraphQL/ParseGraphQLSchema.js @@ -81,7 +81,7 @@ class ParseGraphQLSchema { parseClassMutations.load(this, parseClass, parseClassConfig); } ); - + defaultGraphQLTypes.loadArrayResult(this, parseClasses); defaultGraphQLQueries.load(this); defaultGraphQLMutations.load(this); diff --git a/src/GraphQL/loaders/defaultGraphQLTypes.js b/src/GraphQL/loaders/defaultGraphQLTypes.js index 7961132fa8..d1dcef06e3 100644 --- a/src/GraphQL/loaders/defaultGraphQLTypes.js +++ b/src/GraphQL/loaders/defaultGraphQLTypes.js @@ -12,6 +12,7 @@ import { GraphQLList, GraphQLInputObjectType, GraphQLBoolean, + GraphQLUnionType, } from 'graphql'; import { GraphQLUpload } from 'graphql-upload'; @@ -1020,6 +1021,56 @@ const SIGN_UP_RESULT = new GraphQLObjectType({ }, }); +const ElEMENT = new GraphQLObjectType({ + name: 'Element', + description: + 'The SignUpResult object type is used in the users sign up mutation to return the data of the recent created user.', + fields: { + value: { + description: 'Return the value of the element in the array', + type: new GraphQLNonNull(ANY), + }, + }, +}); + +// Default static union type, we update types and resolveType function later +const ARRAY_RESULT = new GraphQLUnionType({ + name: 'ArrayResult', + description: + 'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments', + types: () => [ElEMENT], + resolveType: () => { + return ElEMENT; + }, +}); + +const loadArrayResult = (parseGraphQLSchema, parseClasses) => { + const ArrayResultTypeIndex = parseGraphQLSchema.graphQLTypes.findIndex( + type => type.name === 'ArrayResult' + ); + const classTypes = parseClasses.map( + parseClass => + parseGraphQLSchema.parseClassTypes[parseClass.className] + .classGraphQLOutputType + ); + parseGraphQLSchema.graphQLTypes[ArrayResultTypeIndex]._types = () => [ + ElEMENT, + ...classTypes, + ]; + parseGraphQLSchema.graphQLTypes[ArrayResultTypeIndex].resolveType = value => { + if (value.__type === 'Object' && value.className && value.objectId) { + if (parseGraphQLSchema.parseClassTypes[value.className]) { + return parseGraphQLSchema.parseClassTypes[value.className] + .classGraphQLOutputType; + } else { + return ElEMENT; + } + } else { + return ElEMENT; + } + }; +}; + const load = parseGraphQLSchema => { parseGraphQLSchema.graphQLTypes.push(GraphQLUpload); parseGraphQLSchema.graphQLTypes.push(ANY); @@ -1056,6 +1107,8 @@ const load = parseGraphQLSchema => { parseGraphQLSchema.graphQLTypes.push(POLYGON_CONSTRAINT); parseGraphQLSchema.graphQLTypes.push(FIND_RESULT); parseGraphQLSchema.graphQLTypes.push(SIGN_UP_RESULT); + parseGraphQLSchema.graphQLTypes.push(ElEMENT); + parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT); }; export { @@ -1140,5 +1193,8 @@ export { POLYGON_CONSTRAINT, FIND_RESULT, SIGN_UP_RESULT, + ARRAY_RESULT, + ElEMENT, load, + loadArrayResult, }; diff --git a/src/GraphQL/loaders/parseClassMutations.js b/src/GraphQL/loaders/parseClassMutations.js index eed18d39da..d493df2d5f 100644 --- a/src/GraphQL/loaders/parseClassMutations.js +++ b/src/GraphQL/loaders/parseClassMutations.js @@ -1,7 +1,7 @@ import { GraphQLNonNull } from 'graphql'; import getFieldNames from 'graphql-list-fields'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; -import * as parseClassTypes from './parseClassTypes'; +import { extractKeysAndInclude } from '../parseGraphQLUtils'; import * as objectsMutations from './objectsMutations'; import * as objectsQueries from './objectsQueries'; import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController'; @@ -119,9 +119,7 @@ const load = function( info ); const selectedFields = getFieldNames(mutationInfo); - const { keys, include } = parseClassTypes.extractKeysAndInclude( - selectedFields - ); + const { keys, include } = extractKeysAndInclude(selectedFields); const { keys: requiredKeys, needGet } = getOnlyRequiredFields( fields, keys, @@ -180,9 +178,7 @@ const load = function( info ); const selectedFields = getFieldNames(mutationInfo); - const { keys, include } = parseClassTypes.extractKeysAndInclude( - selectedFields - ); + const { keys, include } = extractKeysAndInclude(selectedFields); const { keys: requiredKeys, needGet } = getOnlyRequiredFields( fields, @@ -225,9 +221,7 @@ const load = function( const { objectId } = args; const { config, auth, info } = context; const selectedFields = getFieldNames(mutationInfo); - const { keys, include } = parseClassTypes.extractKeysAndInclude( - selectedFields - ); + const { keys, include } = extractKeysAndInclude(selectedFields); let optimizedObject = {}; const splitedKeys = keys.split(','); diff --git a/src/GraphQL/loaders/parseClassQueries.js b/src/GraphQL/loaders/parseClassQueries.js index 1f02f962fb..0f092240e1 100644 --- a/src/GraphQL/loaders/parseClassQueries.js +++ b/src/GraphQL/loaders/parseClassQueries.js @@ -2,8 +2,8 @@ import { GraphQLNonNull } from 'graphql'; import getFieldNames from 'graphql-list-fields'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as objectsQueries from './objectsQueries'; -import * as parseClassTypes from './parseClassTypes'; import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController'; +import { extractKeysAndInclude } from '../parseGraphQLUtils'; const getParseClassQueryConfig = function( parseClassConfig: ?ParseGraphQLClassConfig @@ -11,6 +11,26 @@ const getParseClassQueryConfig = function( return (parseClassConfig && parseClassConfig.query) || {}; }; +const getQuery = async (className, _source, args, context, queryInfo) => { + const { objectId, readPreference, includeReadPreference } = args; + const { config, auth, info } = context; + const selectedFields = getFieldNames(queryInfo); + + const { keys, include } = extractKeysAndInclude(selectedFields); + + return await objectsQueries.getObject( + className, + objectId, + keys, + include, + readPreference, + includeReadPreference, + config, + auth, + info + ); +}; + const load = function( parseGraphQLSchema, parseClass, @@ -40,25 +60,7 @@ const load = function( type: new GraphQLNonNull(classGraphQLOutputType), async resolve(_source, args, context, queryInfo) { try { - const { objectId, readPreference, includeReadPreference } = args; - const { config, auth, info } = context; - const selectedFields = getFieldNames(queryInfo); - - const { keys, include } = parseClassTypes.extractKeysAndInclude( - selectedFields - ); - - return await objectsQueries.getObject( - className, - objectId, - keys, - include, - readPreference, - includeReadPreference, - config, - auth, - info - ); + return await getQuery(className, _source, args, context, queryInfo); } catch (e) { parseGraphQLSchema.handleError(e); } @@ -86,7 +88,7 @@ const load = function( const { config, auth, info } = context; const selectedFields = getFieldNames(queryInfo); - const { keys, include } = parseClassTypes.extractKeysAndInclude( + const { keys, include } = extractKeysAndInclude( selectedFields .filter(field => field.includes('.')) .map(field => field.slice(field.indexOf('.') + 1)) @@ -118,4 +120,4 @@ const load = function( } }; -export { load }; +export { load, getQuery }; diff --git a/src/GraphQL/loaders/parseClassTypes.js b/src/GraphQL/loaders/parseClassTypes.js index 893c786f0c..8c23bf1222 100644 --- a/src/GraphQL/loaders/parseClassTypes.js +++ b/src/GraphQL/loaders/parseClassTypes.js @@ -14,6 +14,7 @@ import getFieldNames from 'graphql-list-fields'; import * as defaultGraphQLTypes from './defaultGraphQLTypes'; import * as objectsQueries from './objectsQueries'; import { ParseGraphQLClassConfig } from '../../Controllers/ParseGraphQLController'; +import { extractKeysAndInclude } from '../parseGraphQLUtils'; const mapInputType = (parseType, targetClass, parseClassTypes) => { switch (parseType) { @@ -65,7 +66,7 @@ const mapOutputType = (parseType, targetClass, parseClassTypes) => { case 'Boolean': return GraphQLBoolean; case 'Array': - return new GraphQLList(defaultGraphQLTypes.ANY); + return new GraphQLList(defaultGraphQLTypes.ARRAY_RESULT); case 'Object': return defaultGraphQLTypes.OBJECT; case 'Date': @@ -135,33 +136,6 @@ const mapConstraintType = (parseType, targetClass, parseClassTypes) => { } }; -const extractKeysAndInclude = selectedFields => { - selectedFields = selectedFields.filter( - field => !field.includes('__typename') - ); - let keys = undefined; - let include = undefined; - if (selectedFields && selectedFields.length > 0) { - keys = selectedFields.join(','); - include = selectedFields - .reduce((fields, field) => { - fields = fields.slice(); - let pointIndex = field.lastIndexOf('.'); - while (pointIndex > 0) { - const lastField = field.slice(pointIndex + 1); - field = field.slice(0, pointIndex); - if (!fields.includes(field) && lastField !== 'objectId') { - fields.push(field); - } - pointIndex = field.lastIndexOf('.'); - } - return fields; - }, []) - .join(','); - } - return { keys, include }; -}; - const getParseClassTypeConfig = function( parseClassConfig: ?ParseGraphQLClassConfig ) { @@ -626,6 +600,27 @@ const load = ( }, }, }; + } else if (parseClass.fields[field].type === 'Array') { + return { + ...fields, + [field]: { + description: `Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments`, + type, + async resolve(source) { + return source[field].map(async elem => { + if ( + elem.className && + elem.objectId && + elem.__type === 'Object' + ) { + return elem; + } else { + return { value: elem }; + } + }); + }, + }, + }; } else if (type) { return { ...fields, diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index 79f7192e53..5f03e057d4 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -12,3 +12,30 @@ export function toGraphQLError(error) { } return new ApolloError(message, code); } + +export const extractKeysAndInclude = selectedFields => { + selectedFields = selectedFields.filter( + field => !field.includes('__typename') + ); + let keys = undefined; + let include = undefined; + if (selectedFields && selectedFields.length > 0) { + keys = selectedFields.join(','); + include = selectedFields + .reduce((fields, field) => { + fields = fields.slice(); + let pointIndex = field.lastIndexOf('.'); + while (pointIndex > 0) { + const lastField = field.slice(pointIndex + 1); + field = field.slice(0, pointIndex); + if (!fields.includes(field) && lastField !== 'objectId') { + fields.push(field); + } + pointIndex = field.lastIndexOf('.'); + } + return fields; + }, []) + .join(','); + } + return { keys, include }; +}; From f6c912aa032405501bf17732210e6cc94f939589 Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Tue, 13 Aug 2019 03:22:32 +0200 Subject: [PATCH 3/6] Fix Test --- spec/ParseGraphQLServer.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index dff8f9e722..e563e75972 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -1704,7 +1704,7 @@ describe('ParseGraphQLServer', () => { await obj1.save(); obj2.set('someClassField', 'imSomeClassTwo'); - await obj3.save(); + await obj2.save(); //const obj3Relation = obj3.relation('manyRelations') obj3.set('manyRelations', [obj1, obj2]); From ae4b7041500595dbc5a8128bda5051d5cbab4967 Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Tue, 13 Aug 2019 19:33:24 +0200 Subject: [PATCH 4/6] Only select the root field --- src/GraphQL/parseGraphQLUtils.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index 5f03e057d4..a9a4d076b0 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -20,7 +20,9 @@ export const extractKeysAndInclude = selectedFields => { let keys = undefined; let include = undefined; if (selectedFields && selectedFields.length > 0) { - keys = selectedFields.join(','); + keys = [...new Set(selectedFields.map(field => field.split('.')[0]))].join( + ',' + ); include = selectedFields .reduce((fields, field) => { fields = fields.slice(); From 4964e215d75145ee75f13967507d34850f44167d Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Tue, 13 Aug 2019 23:45:52 +0200 Subject: [PATCH 5/6] Requested Changes --- spec/ParseGraphQLServer.spec.js | 121 +++++++++++---------- src/GraphQL/loaders/defaultGraphQLTypes.js | 31 ++++-- src/GraphQL/loaders/parseClassQueries.js | 2 +- src/GraphQL/parseGraphQLUtils.js | 4 +- 4 files changed, 84 insertions(+), 74 deletions(-) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index e563e75972..7dc1346f83 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -1693,78 +1693,83 @@ describe('ParseGraphQLServer', () => { expect(new Date(result.updatedAt)).toEqual(obj.updatedAt); }); - it('should return child objects in array fields', async () => { - const obj1 = new Parse.Object('Customer'); - const obj2 = new Parse.Object('SomeClass'); - const obj3 = new Parse.Object('Customer'); + it_only_db('mongo')( + 'should return child objects in array fields', + async () => { + const obj1 = new Parse.Object('Customer'); + const obj2 = new Parse.Object('SomeClass'); + const obj3 = new Parse.Object('Customer'); - obj1.set('someCustomerField', 'imCustomerOne'); - const arrayField = [42.42, 42, 'string', true]; - obj1.set('arrayField', arrayField); - await obj1.save(); + obj1.set('someCustomerField', 'imCustomerOne'); + const arrayField = [42.42, 42, 'string', true]; + obj1.set('arrayField', arrayField); + await obj1.save(); - obj2.set('someClassField', 'imSomeClassTwo'); - await obj2.save(); + obj2.set('someClassField', 'imSomeClassTwo'); + await obj2.save(); - //const obj3Relation = obj3.relation('manyRelations') - obj3.set('manyRelations', [obj1, obj2]); - await obj3.save(); + //const obj3Relation = obj3.relation('manyRelations') + obj3.set('manyRelations', [obj1, obj2]); + await obj3.save(); - await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); - const result = (await apolloClient.query({ - query: gql` - query GetCustomer($objectId: ID!) { - objects { - getCustomer(objectId: $objectId) { - objectId - manyRelations { - ... on CustomerClass { - objectId - someCustomerField - arrayField { - ... on Element { - value + const result = (await apolloClient.query({ + query: gql` + query GetCustomer($objectId: ID!) { + objects { + getCustomer(objectId: $objectId) { + objectId + manyRelations { + ... on CustomerClass { + objectId + someCustomerField + arrayField { + ... on Element { + value + } } } + ... on SomeClassClass { + objectId + someClassField + } } - ... on SomeClassClass { - objectId - someClassField - } + createdAt + updatedAt } - createdAt - updatedAt } } - } - `, - variables: { - objectId: obj3.id, - }, - })).data.objects.getCustomer; + `, + variables: { + objectId: obj3.id, + }, + })).data.objects.getCustomer; - expect(result.objectId).toEqual(obj3.id); - expect(result.manyRelations.length).toEqual(2); + expect(result.objectId).toEqual(obj3.id); + expect(result.manyRelations.length).toEqual(2); - const customerSubObject = result.manyRelations.find( - o => o.objectId === obj1.id - ); - const someClassSubObject = result.manyRelations.find( - o => o.objectId === obj2.id - ); + const customerSubObject = result.manyRelations.find( + o => o.objectId === obj1.id + ); + const someClassSubObject = result.manyRelations.find( + o => o.objectId === obj2.id + ); - expect(customerSubObject).toBeDefined(); - expect(someClassSubObject).toBeDefined(); - expect(customerSubObject.someCustomerField).toEqual( - 'imCustomerOne' - ); - const formatedArrayField = customerSubObject.arrayField.map( - elem => elem.value - ); - expect(formatedArrayField).toEqual(arrayField); - expect(someClassSubObject.someClassField).toEqual('imSomeClassTwo'); - }); + expect(customerSubObject).toBeDefined(); + expect(someClassSubObject).toBeDefined(); + expect(customerSubObject.someCustomerField).toEqual( + 'imCustomerOne' + ); + const formatedArrayField = customerSubObject.arrayField.map( + elem => elem.value + ); + expect(formatedArrayField).toEqual(arrayField); + expect(someClassSubObject.someClassField).toEqual( + 'imSomeClassTwo' + ); + } + ); it('should respect level permissions', async () => { await prepareData(); diff --git a/src/GraphQL/loaders/defaultGraphQLTypes.js b/src/GraphQL/loaders/defaultGraphQLTypes.js index d1dcef06e3..7659e32ec0 100644 --- a/src/GraphQL/loaders/defaultGraphQLTypes.js +++ b/src/GraphQL/loaders/defaultGraphQLTypes.js @@ -1021,7 +1021,7 @@ const SIGN_UP_RESULT = new GraphQLObjectType({ }, }); -const ElEMENT = new GraphQLObjectType({ +const ELEMENT = new GraphQLObjectType({ name: 'Element', description: 'The SignUpResult object type is used in the users sign up mutation to return the data of the recent created user.', @@ -1038,23 +1038,30 @@ const ARRAY_RESULT = new GraphQLUnionType({ name: 'ArrayResult', description: 'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments', - types: () => [ElEMENT], + types: () => [ELEMENT], resolveType: () => { - return ElEMENT; + return ELEMENT; }, }); const loadArrayResult = (parseGraphQLSchema, parseClasses) => { const ArrayResultTypeIndex = parseGraphQLSchema.graphQLTypes.findIndex( - type => type.name === 'ArrayResult' + type => type.name === 'ArrayResult' && type instanceof GraphQLUnionType ); - const classTypes = parseClasses.map( - parseClass => + const classTypes = parseClasses + .filter(parseClass => parseGraphQLSchema.parseClassTypes[parseClass.className] .classGraphQLOutputType - ); + ? true + : false + ) + .map( + parseClass => + parseGraphQLSchema.parseClassTypes[parseClass.className] + .classGraphQLOutputType + ); parseGraphQLSchema.graphQLTypes[ArrayResultTypeIndex]._types = () => [ - ElEMENT, + ELEMENT, ...classTypes, ]; parseGraphQLSchema.graphQLTypes[ArrayResultTypeIndex].resolveType = value => { @@ -1063,10 +1070,10 @@ const loadArrayResult = (parseGraphQLSchema, parseClasses) => { return parseGraphQLSchema.parseClassTypes[value.className] .classGraphQLOutputType; } else { - return ElEMENT; + return ELEMENT; } } else { - return ElEMENT; + return ELEMENT; } }; }; @@ -1107,7 +1114,7 @@ const load = parseGraphQLSchema => { parseGraphQLSchema.graphQLTypes.push(POLYGON_CONSTRAINT); parseGraphQLSchema.graphQLTypes.push(FIND_RESULT); parseGraphQLSchema.graphQLTypes.push(SIGN_UP_RESULT); - parseGraphQLSchema.graphQLTypes.push(ElEMENT); + parseGraphQLSchema.graphQLTypes.push(ELEMENT); parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT); }; @@ -1194,7 +1201,7 @@ export { FIND_RESULT, SIGN_UP_RESULT, ARRAY_RESULT, - ElEMENT, + ELEMENT, load, loadArrayResult, }; diff --git a/src/GraphQL/loaders/parseClassQueries.js b/src/GraphQL/loaders/parseClassQueries.js index 0f092240e1..5d0bc02015 100644 --- a/src/GraphQL/loaders/parseClassQueries.js +++ b/src/GraphQL/loaders/parseClassQueries.js @@ -120,4 +120,4 @@ const load = function( } }; -export { load, getQuery }; +export { load }; diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js index a9a4d076b0..5f03e057d4 100644 --- a/src/GraphQL/parseGraphQLUtils.js +++ b/src/GraphQL/parseGraphQLUtils.js @@ -20,9 +20,7 @@ export const extractKeysAndInclude = selectedFields => { let keys = undefined; let include = undefined; if (selectedFields && selectedFields.length > 0) { - keys = [...new Set(selectedFields.map(field => field.split('.')[0]))].join( - ',' - ); + keys = selectedFields.join(','); include = selectedFields .reduce((fields, field) => { fields = fields.slice(); From ecd8237b36c16e3a95b7125c7ec9b50ede948684 Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Wed, 14 Aug 2019 11:28:02 +0200 Subject: [PATCH 6/6] Lazy Loaded ArrayResult --- src/GraphQL/loaders/defaultGraphQLTypes.js | 43 +++++++++------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/GraphQL/loaders/defaultGraphQLTypes.js b/src/GraphQL/loaders/defaultGraphQLTypes.js index 7659e32ec0..58115b9198 100644 --- a/src/GraphQL/loaders/defaultGraphQLTypes.js +++ b/src/GraphQL/loaders/defaultGraphQLTypes.js @@ -1034,20 +1034,9 @@ const ELEMENT = new GraphQLObjectType({ }); // Default static union type, we update types and resolveType function later -const ARRAY_RESULT = new GraphQLUnionType({ - name: 'ArrayResult', - description: - 'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments', - types: () => [ELEMENT], - resolveType: () => { - return ELEMENT; - }, -}); +let ARRAY_RESULT; const loadArrayResult = (parseGraphQLSchema, parseClasses) => { - const ArrayResultTypeIndex = parseGraphQLSchema.graphQLTypes.findIndex( - type => type.name === 'ArrayResult' && type instanceof GraphQLUnionType - ); const classTypes = parseClasses .filter(parseClass => parseGraphQLSchema.parseClassTypes[parseClass.className] @@ -1060,22 +1049,25 @@ const loadArrayResult = (parseGraphQLSchema, parseClasses) => { parseGraphQLSchema.parseClassTypes[parseClass.className] .classGraphQLOutputType ); - parseGraphQLSchema.graphQLTypes[ArrayResultTypeIndex]._types = () => [ - ELEMENT, - ...classTypes, - ]; - parseGraphQLSchema.graphQLTypes[ArrayResultTypeIndex].resolveType = value => { - if (value.__type === 'Object' && value.className && value.objectId) { - if (parseGraphQLSchema.parseClassTypes[value.className]) { - return parseGraphQLSchema.parseClassTypes[value.className] - .classGraphQLOutputType; + ARRAY_RESULT = new GraphQLUnionType({ + name: 'ArrayResult', + description: + 'Use Inline Fragment on Array to get results: https://graphql.org/learn/queries/#inline-fragments', + types: () => [ELEMENT, ...classTypes], + resolveType: value => { + if (value.__type === 'Object' && value.className && value.objectId) { + if (parseGraphQLSchema.parseClassTypes[value.className]) { + return parseGraphQLSchema.parseClassTypes[value.className] + .classGraphQLOutputType; + } else { + return ELEMENT; + } } else { return ELEMENT; } - } else { - return ELEMENT; - } - }; + }, + }); + parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT); }; const load = parseGraphQLSchema => { @@ -1115,7 +1107,6 @@ const load = parseGraphQLSchema => { parseGraphQLSchema.graphQLTypes.push(FIND_RESULT); parseGraphQLSchema.graphQLTypes.push(SIGN_UP_RESULT); parseGraphQLSchema.graphQLTypes.push(ELEMENT); - parseGraphQLSchema.graphQLTypes.push(ARRAY_RESULT); }; export {