From 2083840d3a78d1b4dc5e4d8e0e6c4f19397dd25d Mon Sep 17 00:00:00 2001 From: Antoine Cormouls Date: Wed, 7 Feb 2024 15:14:37 +0100 Subject: [PATCH 1/3] fix: required field on transform types --- spec/ParseGraphQLServer.spec.js | 68 ++++++++++++++++++++++++++++ src/GraphQL/transformers/mutation.js | 16 ++++--- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 022fb99fd2..8799fae7ed 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -9546,6 +9546,74 @@ describe('ParseGraphQLServer', () => { } }); + it_only_node_version('<17')('should support files on required file', async () => { + try { + parseServer = await global.reconfigureServer({ + publicServerURL: 'http://localhost:13377/parse', + }); + + const schemaController = await parseServer.config.databaseController.loadSchema(); + await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', { + someField: { type: 'File', required: true }, + }); + + await resetGraphQLCache(); + + await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); + + const body = new FormData(); + body.append( + 'operations', + JSON.stringify({ + query: ` + mutation CreateSomeObject( + $fields: CreateSomeClassWithRequiredFileFieldsInput + ) { + createSomeClassWithRequiredFile( + input: { fields: $fields } + ) { + someClassWithRequiredFile { + id + someField { + name + url + } + } + } + } + `, + variables: { + fields: { + someField: { upload: null }, + }, + }, + }) + ); + body.append('map', JSON.stringify({ 1: ['variables.fields.someField.upload'] })); + body.append('1', 'My File Content', { + filename: 'myFileName.txt', + contentType: 'text/plain', + }); + + const res = await fetch('http://localhost:13377/graphql', { + method: 'POST', + headers, + body, + }); + expect(res.status).toEqual(200); + const resText = await res.text(); + const result = JSON.parse(resText); + expect( + result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.name + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + expect( + result.data.createSomeClassWithRequiredFile.someClassWithRequiredFile.someField.url + ).toEqual(jasmine.stringMatching(/_myFileName.txt$/)); + } catch (e) { + handleError(e); + } + }); + it_only_node_version('<17')('should not upload if file is too large', async () => { parseGraphQLServer.parseServer.config.maxUploadSize = '1kb'; diff --git a/src/GraphQL/transformers/mutation.js b/src/GraphQL/transformers/mutation.js index 5b72d6f05d..accb1528ca 100644 --- a/src/GraphQL/transformers/mutation.js +++ b/src/GraphQL/transformers/mutation.js @@ -1,7 +1,6 @@ import Parse from 'parse/node'; import { fromGlobalId } from 'graphql-relay'; import { handleUpload } from '../loaders/filesMutations'; -import * as defaultGraphQLTypes from '../loaders/defaultGraphQLTypes'; import * as objectsMutations from '../helpers/objectsMutations'; const transformTypes = async ( @@ -28,25 +27,28 @@ const transformTypes = async ( inputTypeField = classGraphQLUpdateTypeFields[field]; } if (inputTypeField) { - switch (true) { - case inputTypeField.type === defaultGraphQLTypes.GEO_POINT_INPUT: + const parseFieldType = parseClass.fields[field].type; + switch (parseFieldType) { + case 'GeoPoint': if (fields[field] === null) { fields[field] = { __op: 'Delete' }; break; } fields[field] = transformers.geoPoint(fields[field]); break; - case inputTypeField.type === defaultGraphQLTypes.POLYGON_INPUT: + case 'Polygon': if (fields[field] === null) { fields[field] = { __op: 'Delete' }; break; } fields[field] = transformers.polygon(fields[field]); break; - case inputTypeField.type === defaultGraphQLTypes.FILE_INPUT: + case 'File': + // We need to use the originalFields to handle the file upload + // since fields are a deepcopy and do not keep the file object fields[field] = await transformers.file(fields[field], req); break; - case parseClass.fields[field].type === 'Relation': + case 'Relation': fields[field] = await transformers.relation( parseClass.fields[field].targetClass, field, @@ -55,7 +57,7 @@ const transformTypes = async ( req ); break; - case parseClass.fields[field].type === 'Pointer': + case 'Pointer': if (fields[field] === null) { fields[field] = { __op: 'Delete' }; break; From 8be40a2104d660a0dc94e8263d44c39970137d6c Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:01:17 +0100 Subject: [PATCH 2/3] lint fix --- spec/AuthenticationAdaptersV2.spec.js | 2 +- spec/ParseGraphQLServer.spec.js | 66 +++++++++++++-------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/spec/AuthenticationAdaptersV2.spec.js b/spec/AuthenticationAdaptersV2.spec.js index 41c76b1f2b..e9486187ef 100644 --- a/spec/AuthenticationAdaptersV2.spec.js +++ b/spec/AuthenticationAdaptersV2.spec.js @@ -964,7 +964,7 @@ describe('Auth Adapter features', () => { allowExpiredAuthDataToken: false, }); logger = require('../lib/logger').logger; - spyOn(logger, 'error').and.callFake(() => { }); + spyOn(logger, 'error').and.callFake(() => {}); user = new Parse.User(); await user.save({ authData: { modernAdapter: { id: 'modernAdapter' } } }); const user2 = new Parse.User(); diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 3729f9d18d..5259e459aa 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -88,8 +88,8 @@ describe('ParseGraphQLServer', () => { it('should initialize parseGraphQLSchema with a log controller', async () => { const loggerAdapter = { - log: () => { }, - error: () => { }, + log: () => {}, + error: () => {}, }; const parseServer = await global.reconfigureServer({ loggerAdapter, @@ -124,10 +124,10 @@ describe('ParseGraphQLServer', () => { info: new Object(), config: new Object(), auth: new Object(), - get: () => { }, + get: () => {}, }; const res = { - set: () => { }, + set: () => {}, }; it("should return schema and context with req's info, config and auth", async () => { @@ -473,8 +473,8 @@ describe('ParseGraphQLServer', () => { }, }, }); - spyOn(console, 'warn').and.callFake(() => { }); - spyOn(console, 'error').and.callFake(() => { }); + spyOn(console, 'warn').and.callFake(() => {}); + spyOn(console, 'error').and.callFake(() => {}); }); afterEach(async () => { @@ -853,7 +853,7 @@ describe('ParseGraphQLServer', () => { }); it('should have clientMutationId in call function input', async () => { - Parse.Cloud.define('hello', () => { }); + Parse.Cloud.define('hello', () => {}); const callFunctionInputFields = ( await apolloClient.query({ @@ -875,7 +875,7 @@ describe('ParseGraphQLServer', () => { }); it('should have clientMutationId in call function payload', async () => { - Parse.Cloud.define('hello', () => { }); + Parse.Cloud.define('hello', () => {}); const callFunctionPayloadFields = ( await apolloClient.query({ @@ -6541,7 +6541,7 @@ describe('ParseGraphQLServer', () => { ); expect( (await deleteObject(object4.className, object4.id)).data.delete[ - object4.className.charAt(0).toLowerCase() + object4.className.slice(1) + object4.className.charAt(0).toLowerCase() + object4.className.slice(1) ] ).toEqual({ objectId: object4.id, __typename: 'PublicClass' }); await expectAsync(object4.fetch({ useMasterKey: true })).toBeRejectedWith( @@ -7432,9 +7432,9 @@ describe('ParseGraphQLServer', () => { it('should send reset password', async () => { const clientMutationId = uuidv4(); const emailAdapter = { - sendVerificationEmail: () => { }, + sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => { }, + sendMail: () => {}, }; parseServer = await global.reconfigureServer({ appName: 'test', @@ -7472,11 +7472,11 @@ describe('ParseGraphQLServer', () => { const clientMutationId = uuidv4(); let resetPasswordToken; const emailAdapter = { - sendVerificationEmail: () => { }, + sendVerificationEmail: () => {}, sendPasswordResetEmail: ({ link }) => { resetPasswordToken = link.split('token=')[1].split('&')[0]; }, - sendMail: () => { }, + sendMail: () => {}, }; parseServer = await global.reconfigureServer({ appName: 'test', @@ -7541,9 +7541,9 @@ describe('ParseGraphQLServer', () => { it('should send verification email again', async () => { const clientMutationId = uuidv4(); const emailAdapter = { - sendVerificationEmail: () => { }, + sendVerificationEmail: () => {}, sendPasswordResetEmail: () => Promise.resolve(), - sendMail: () => { }, + sendMail: () => {}, }; parseServer = await global.reconfigureServer({ appName: 'test', @@ -11142,25 +11142,25 @@ describe('ParseGraphQLServer', () => { }, }); const SomeClassType = new GraphQLObjectType({ - name: 'SomeClass', - fields: { - nameUpperCase: { - type: new GraphQLNonNull(GraphQLString), - resolve: p => p.name.toUpperCase(), - }, - type: { type: TypeEnum }, - language: { - type: new GraphQLEnumType({ - name: 'LanguageEnum', - values: { - fr: { value: 'fr' }, - en: { value: 'en' }, - }, - }), - resolve: () => 'fr', + name: 'SomeClass', + fields: { + nameUpperCase: { + type: new GraphQLNonNull(GraphQLString), + resolve: p => p.name.toUpperCase(), + }, + type: { type: TypeEnum }, + language: { + type: new GraphQLEnumType({ + name: 'LanguageEnum', + values: { + fr: { value: 'fr' }, + en: { value: 'en' }, + }, + }), + resolve: () => 'fr', + }, }, - }, - }), + }), parseGraphQLServer = new ParseGraphQLServer(parseServer, { graphQLPath: '/graphql', graphQLCustomTypeDefs: new GraphQLSchema({ From 2b4fe38c44f4cc2081a62cdfddc80cf0465a4de3 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Wed, 20 Mar 2024 23:02:17 +0100 Subject: [PATCH 3/3] Update ParseGraphQLServer.spec.js --- spec/ParseGraphQLServer.spec.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 5259e459aa..dafe1a3ee2 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -9553,14 +9553,11 @@ describe('ParseGraphQLServer', () => { parseServer = await global.reconfigureServer({ publicServerURL: 'http://localhost:13377/parse', }); - const schemaController = await parseServer.config.databaseController.loadSchema(); await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', { someField: { type: 'File', required: true }, }); - await resetGraphQLCache(); - await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear(); const body = new FormData();