From 448c203e137ef9b1e598a851c2bcdf8458670277 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 31 Mar 2023 17:41:51 +0200 Subject: [PATCH 01/13] Add tests for handling different return types from custom handlers --- test/resources/custom-handlers/server.js | 6 + .../custom-handlers/srv/return-types.cds | 21 ++ .../custom-handlers/srv/return-types.js | 9 + test/tests/custom-handlers.test.js | 246 ++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 test/resources/custom-handlers/server.js create mode 100644 test/resources/custom-handlers/srv/return-types.cds create mode 100644 test/resources/custom-handlers/srv/return-types.js create mode 100644 test/tests/custom-handlers.test.js diff --git a/test/resources/custom-handlers/server.js b/test/resources/custom-handlers/server.js new file mode 100644 index 00000000..6808b2bb --- /dev/null +++ b/test/resources/custom-handlers/server.js @@ -0,0 +1,6 @@ +const cds = require('@sap/cds') +const path = require('path') + +cds.env.protocols = { + graphql: { path: '/graphql', impl: path.join(__dirname, '../../../index.js') } +} \ No newline at end of file diff --git a/test/resources/custom-handlers/srv/return-types.cds b/test/resources/custom-handlers/srv/return-types.cds new file mode 100644 index 00000000..663cb32c --- /dev/null +++ b/test/resources/custom-handlers/srv/return-types.cds @@ -0,0 +1,21 @@ +service ReturnTypesService { + entity Integer { + key id : UUID; + string : cds.String; + } + + entity String { + key id : UUID; + string : cds.String; + } + + entity Object { + key id : UUID; + string : cds.String; + } + + entity Array { + key id : UUID; + string : cds.String; + } +} diff --git a/test/resources/custom-handlers/srv/return-types.js b/test/resources/custom-handlers/srv/return-types.js new file mode 100644 index 00000000..586c1f9d --- /dev/null +++ b/test/resources/custom-handlers/srv/return-types.js @@ -0,0 +1,9 @@ +module.exports = srv => { + const string = 'foo' + const id = '0557a188-326e-4dcb-999b-e1acf7979fa3' + + srv.on('*', 'Integer', () => 999) + srv.on('*', 'String', () => string) + srv.on('*', 'Object', () => ({ id, string })) + srv.on('*', 'Array', () => [{ id, string }, { id, string }]) +} \ No newline at end of file diff --git a/test/tests/custom-handlers.test.js b/test/tests/custom-handlers.test.js new file mode 100644 index 00000000..75cb93d9 --- /dev/null +++ b/test/tests/custom-handlers.test.js @@ -0,0 +1,246 @@ +describe('graphql - custom handlers', () => { + const cds = require('@sap/cds/lib') + const path = require('path') + const { gql } = require('../util') + + const { axios, POST } = cds.test(path.join(__dirname, '../resources/custom-handlers')) + // Prevent axios from throwing errors for non 2xx status codes + axios.defaults.validateStatus = false + + describe('Handling of different return types returned by custom handlers', () => { + const id = '0557a188-326e-4dcb-999b-e1acf7979fa3' + const string = 'foo' + + test('Integer, String, Object, and Array returned for CREATE custom handler', async () => { + const query = gql` + mutation { + ReturnTypesService { + Integer { + create(input: {}) { + id + string + } + } + String { + create(input: {}) { + id + string + } + } + Object { + create(input: {}) { + id + string + } + } + Array { + create(input: {}) { + id + string + } + } + } + } + ` + const data = { + ReturnTypesService: { + Integer: { + create: null + }, + String: { + create: null + }, + Object: { + create: [{ id, string }] + }, + Array: { + create: [ + { id, string }, + { id, string } + ] + } + } + } + const errors = [ + { + locations: [{ column: 15, line: 5 }], + message: 'Expected Iterable, but did not find one for field "ReturnTypesService_Integer_input.create".', + path: ['ReturnTypesService', 'Integer', 'create'] + }, + { + locations: [{ column: 15, line: 11 }], + message: 'Expected Iterable, but did not find one for field "ReturnTypesService_String_input.create".', + path: ['ReturnTypesService', 'String', 'create'] + } + ] + const response = await POST('/graphql', { query }) + expect(response.data).toEqual({ data, errors }) + }) + + test('Integer, String, Object, and Array returned for READ custom handler', async () => { + const query = gql` + query { + ReturnTypesService { + Integer { + nodes { + id + string + } + } + String { + nodes { + id + string + } + } + Object { + nodes { + id + string + } + } + Array { + nodes { + id + string + } + } + } + } + ` + const data = { + ReturnTypesService: { + Integer: { + nodes: null + }, + String: { + nodes: null + }, + Object: { + nodes: [{ id, string }] + }, + Array: { + nodes: [ + { id, string }, + { id, string } + ] + } + } + } + const response = await POST('/graphql', { query }) + expect(response.data).toEqual({ data }) + }) + + test('Integer, String, Object, and Array returned for UPDATE custom handler', async () => { + const query = gql` + mutation { + ReturnTypesService { + Integer { + update(filter: [], input: {}) { + id + string + } + } + String { + update(filter: [], input: {}) { + id + string + } + } + Object { + update(filter: [], input: {}) { + id + string + } + } + Array { + update(filter: [], input: {}) { + id + string + } + } + } + } + ` + const data = { + ReturnTypesService: { + Integer: { + update: null + }, + String: { + update: null + }, + Object: { + update: [{ id, string }] + }, + Array: { + update: [ + { id, string }, + { id, string } + ] + } + } + } + const errors = [ + { + locations: [{ column: 15, line: 5 }], + message: 'Expected Iterable, but did not find one for field "ReturnTypesService_Integer_input.update".', + path: ['ReturnTypesService', 'Integer', 'update'] + }, + { + locations: [{ column: 15, line: 11 }], + message: 'Expected Iterable, but did not find one for field "ReturnTypesService_String_input.update".', + path: ['ReturnTypesService', 'String', 'update'] + } + ] + const response = await POST('/graphql', { query }) + expect(response.data).toEqual({ data, errors }) + }) + + test('Integer, String, Object, and Array returned for DELETE custom handler', async () => { + const query = gql` + mutation { + ReturnTypesService { + Integer { + delete(filter: []) + } + String { + delete(filter: []) + } + Object { + delete(filter: []) + } + Array { + delete(filter: []) + } + } + } + ` + const data = { + ReturnTypesService: { + Integer: { + delete: 999 + }, + String: { + delete: null + }, + Object: { + delete: 1 + }, + Array: { + delete: 2 + } + } + } + const errors = [ + { + locations: [{ column: 15, line: 8 }], + message: 'Int cannot represent non-integer value: "foo"', + path: ['ReturnTypesService', 'String', 'delete'] + } + ] + const response = await POST('/graphql', { query }) + expect(response.data).toEqual({ data, errors }) + }) + }) +}) From 73c5dcfb16ed4561633711fadfbd7035ae3d50cb Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 31 Mar 2023 17:42:05 +0200 Subject: [PATCH 02/13] Fix test description typo --- test/tests/concurrency.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests/concurrency.test.js b/test/tests/concurrency.test.js index fd14c010..93ccfc77 100644 --- a/test/tests/concurrency.test.js +++ b/test/tests/concurrency.test.js @@ -1,4 +1,4 @@ -describe('graphql resolver concurrency', () => { +describe('graphql - resolver concurrency', () => { const cds = require('@sap/cds/lib') const path = require('path') const { gql } = require('../util') From a836929f2b2db6734d39044f850bdedd41f90423 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 31 Mar 2023 17:43:49 +0200 Subject: [PATCH 03/13] Return 1 for objects and array.length for arrays set in delete custom handlers --- lib/resolvers/crud/delete.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/resolvers/crud/delete.js b/lib/resolvers/crud/delete.js index db235697..aaac1533 100644 --- a/lib/resolvers/crud/delete.js +++ b/lib/resolvers/crud/delete.js @@ -16,5 +16,9 @@ module.exports = async (service, entity, selection) => { else throw e } + // Custom handlers can return non-numeric results for delete + if (Array.isArray(result)) return result.length + if (typeof result === 'object') return 1 + return result } From 26dc385630800549aa5257472a781ecb45e099ed Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Fri, 31 Mar 2023 17:45:11 +0200 Subject: [PATCH 04/13] Only wrap objects in arrays instead of all types in CUD resolvers --- lib/resolvers/crud/create.js | 2 +- lib/resolvers/crud/read.js | 4 +++- lib/resolvers/crud/update.js | 11 ++++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/resolvers/crud/create.js b/lib/resolvers/crud/create.js index 3b98a451..922bfdad 100644 --- a/lib/resolvers/crud/create.js +++ b/lib/resolvers/crud/create.js @@ -12,7 +12,7 @@ module.exports = async (service, entity, selection) => { query.entries(entries) const result = await service.run(query) - const resultInArray = Array.isArray(result) ? result : [result] + const resultInArray = typeof result === 'object' && !Array.isArray(result) ? [result] : result return formatResult(selection, resultInArray, true) } diff --git a/lib/resolvers/crud/read.js b/lib/resolvers/crud/read.js index 2a009fb1..d3212901 100644 --- a/lib/resolvers/crud/read.js +++ b/lib/resolvers/crud/read.js @@ -27,5 +27,7 @@ module.exports = async (service, entity, selection) => { const result = await service.run(query) - return formatResult(selection, result) + const resultInArray = typeof result === 'object' && !Array.isArray(result) ? [result] : result + + return formatResult(selection, resultInArray) } diff --git a/lib/resolvers/crud/update.js b/lib/resolvers/crud/update.js index c9ea4f15..794663cc 100644 --- a/lib/resolvers/crud/update.js +++ b/lib/resolvers/crud/update.js @@ -30,8 +30,13 @@ module.exports = async (service, entity, selection) => { return tx.run(query) }) - // Merge selected fields with updated data - const mergedResults = resultBeforeUpdate.map(original => ({ ...original, ...result })) + let mergedResults = result + if (Array.isArray(resultBeforeUpdate)) { + // Merge selected fields with updated data + mergedResults = resultBeforeUpdate.map(original => ({ ...original, ...result })) + } - return formatResult(selection, mergedResults, true) + const resultInArray = typeof result === 'object' && !Array.isArray(result) ? [result] : result + + return formatResult(selection, resultInArray, true) } From 0979766529886974ca1d871cb6fc1bb26a46adea Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 14:43:48 +0200 Subject: [PATCH 05/13] Use mergedResults instead of result in update --- lib/resolvers/crud/update.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/resolvers/crud/update.js b/lib/resolvers/crud/update.js index 794663cc..277ed8a8 100644 --- a/lib/resolvers/crud/update.js +++ b/lib/resolvers/crud/update.js @@ -36,7 +36,8 @@ module.exports = async (service, entity, selection) => { mergedResults = resultBeforeUpdate.map(original => ({ ...original, ...result })) } - const resultInArray = typeof result === 'object' && !Array.isArray(result) ? [result] : result + const resultInArray = + typeof mergedResults === 'object' && !Array.isArray(mergedResults) ? [mergedResults] : mergedResults return formatResult(selection, resultInArray, true) } From c6a6306c384d69a5c589122e0258e4a162a1068d Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 14:55:45 +0200 Subject: [PATCH 06/13] Use isPlainObject helper instead of typeof object --- lib/resolvers/crud/create.js | 3 ++- lib/resolvers/crud/delete.js | 3 ++- lib/resolvers/crud/read.js | 3 ++- lib/resolvers/crud/update.js | 4 ++-- lib/resolvers/parse/ast/fromObject.js | 2 +- lib/resolvers/parse/ast/result.js | 2 +- lib/resolvers/parse/ast/util/index.js | 3 --- lib/resolvers/utils/index.js | 4 +++- 8 files changed, 13 insertions(+), 11 deletions(-) delete mode 100644 lib/resolvers/parse/ast/util/index.js diff --git a/lib/resolvers/crud/create.js b/lib/resolvers/crud/create.js index 922bfdad..fe5a6787 100644 --- a/lib/resolvers/crud/create.js +++ b/lib/resolvers/crud/create.js @@ -2,6 +2,7 @@ const { INSERT } = require('@sap/cds/lib').ql const { ARGS } = require('../../constants') const formatResult = require('../parse/ast/result') const { getArgumentByName, astToEntries } = require('../parse/ast2cqn') +const { isPlainObject } = require('../utils') const { entriesStructureToEntityStructure } = require('./utils') module.exports = async (service, entity, selection) => { @@ -12,7 +13,7 @@ module.exports = async (service, entity, selection) => { query.entries(entries) const result = await service.run(query) - const resultInArray = typeof result === 'object' && !Array.isArray(result) ? [result] : result + const resultInArray = isPlainObject(result) && !Array.isArray(result) ? [result] : result return formatResult(selection, resultInArray, true) } diff --git a/lib/resolvers/crud/delete.js b/lib/resolvers/crud/delete.js index aaac1533..8724a873 100644 --- a/lib/resolvers/crud/delete.js +++ b/lib/resolvers/crud/delete.js @@ -1,6 +1,7 @@ const { DELETE } = require('@sap/cds/lib').ql const { ARGS } = require('../../constants') const { getArgumentByName, astToWhere } = require('../parse/ast2cqn') +const { isPlainObject } = require('../utils') module.exports = async (service, entity, selection) => { let query = DELETE.from(entity) @@ -18,7 +19,7 @@ module.exports = async (service, entity, selection) => { // Custom handlers can return non-numeric results for delete if (Array.isArray(result)) return result.length - if (typeof result === 'object') return 1 + if (isPlainObject(result)) return 1 return result } diff --git a/lib/resolvers/crud/read.js b/lib/resolvers/crud/read.js index d3212901..e1465068 100644 --- a/lib/resolvers/crud/read.js +++ b/lib/resolvers/crud/read.js @@ -2,6 +2,7 @@ const { SELECT } = require('@sap/cds/lib').ql const { ARGS, CONNECTION_FIELDS } = require('../../constants') const { getArgumentByName, astToColumns, astToWhere, astToOrderBy, astToLimit } = require('../parse/ast2cqn') const formatResult = require('../parse/ast/result') +const { isPlainObject } = require('../utils') module.exports = async (service, entity, selection) => { const selections = selection.selectionSet.selections @@ -27,7 +28,7 @@ module.exports = async (service, entity, selection) => { const result = await service.run(query) - const resultInArray = typeof result === 'object' && !Array.isArray(result) ? [result] : result + const resultInArray = isPlainObject(result) && !Array.isArray(result) ? [result] : result return formatResult(selection, resultInArray) } diff --git a/lib/resolvers/crud/update.js b/lib/resolvers/crud/update.js index 277ed8a8..6ebe6ebb 100644 --- a/lib/resolvers/crud/update.js +++ b/lib/resolvers/crud/update.js @@ -2,6 +2,7 @@ const { SELECT, UPDATE } = require('@sap/cds/lib').ql const { ARGS } = require('../../constants') const formatResult = require('../parse/ast/result') const { getArgumentByName, astToColumns, astToWhere, astToEntries } = require('../parse/ast2cqn') +const { isPlainObject } = require('../utils') const { entriesStructureToEntityStructure } = require('./utils') module.exports = async (service, entity, selection) => { @@ -36,8 +37,7 @@ module.exports = async (service, entity, selection) => { mergedResults = resultBeforeUpdate.map(original => ({ ...original, ...result })) } - const resultInArray = - typeof mergedResults === 'object' && !Array.isArray(mergedResults) ? [mergedResults] : mergedResults + const resultInArray = isPlainObject(mergedResults) && !Array.isArray(mergedResults) ? [mergedResults] : mergedResults return formatResult(selection, resultInArray, true) } diff --git a/lib/resolvers/parse/ast/fromObject.js b/lib/resolvers/parse/ast/fromObject.js index fa35feca..02a8a809 100644 --- a/lib/resolvers/parse/ast/fromObject.js +++ b/lib/resolvers/parse/ast/fromObject.js @@ -1,5 +1,5 @@ const { Kind } = require('graphql') -const { isPlainObject } = require('./util') +const { isPlainObject } = require('../../utils') const _nullValue = { kind: Kind.NULL } diff --git a/lib/resolvers/parse/ast/result.js b/lib/resolvers/parse/ast/result.js index c134e09c..24647e7b 100644 --- a/lib/resolvers/parse/ast/result.js +++ b/lib/resolvers/parse/ast/result.js @@ -1,6 +1,6 @@ const { CONNECTION_FIELDS } = require('../../../constants') +const { isPlainObject } = require('../../utils') const { getPotentiallyNestedNodesSelections } = require('../util') -const { isPlainObject } = require('./util') const formatResult = (field, result, skipTopLevelConnection) => { const _formatObject = (selections, object) => { diff --git a/lib/resolvers/parse/ast/util/index.js b/lib/resolvers/parse/ast/util/index.js deleted file mode 100644 index d7970344..00000000 --- a/lib/resolvers/parse/ast/util/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const isPlainObject = value => value !== null && typeof value === 'object' && !Buffer.isBuffer(value) - -module.exports = { isPlainObject } diff --git a/lib/resolvers/utils/index.js b/lib/resolvers/utils/index.js index 1ac9c375..36e1c586 100644 --- a/lib/resolvers/utils/index.js +++ b/lib/resolvers/utils/index.js @@ -1,3 +1,5 @@ +const isPlainObject = value => value !== null && typeof value === 'object' && !Buffer.isBuffer(value) + const _ensureError = error => (error instanceof Error ? error : new Error(error)) const setResponse = async (response, key, value) => { @@ -8,4 +10,4 @@ const setResponse = async (response, key, value) => { } } -module.exports = { setResponse } +module.exports = { isPlainObject, setResponse } From 7b98a2edf877b26d2fcacd4e57531240cb9f9a1d Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 15:43:34 +0200 Subject: [PATCH 07/13] Add is not array check to isPlainObject helper --- lib/resolvers/crud/create.js | 2 +- lib/resolvers/crud/delete.js | 2 +- lib/resolvers/crud/read.js | 2 +- lib/resolvers/crud/update.js | 2 +- lib/resolvers/utils/index.js | 3 ++- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/resolvers/crud/create.js b/lib/resolvers/crud/create.js index fe5a6787..25557d96 100644 --- a/lib/resolvers/crud/create.js +++ b/lib/resolvers/crud/create.js @@ -13,7 +13,7 @@ module.exports = async (service, entity, selection) => { query.entries(entries) const result = await service.run(query) - const resultInArray = isPlainObject(result) && !Array.isArray(result) ? [result] : result + const resultInArray = isPlainObject(result) ? [result] : result return formatResult(selection, resultInArray, true) } diff --git a/lib/resolvers/crud/delete.js b/lib/resolvers/crud/delete.js index 8724a873..fc239062 100644 --- a/lib/resolvers/crud/delete.js +++ b/lib/resolvers/crud/delete.js @@ -18,8 +18,8 @@ module.exports = async (service, entity, selection) => { } // Custom handlers can return non-numeric results for delete - if (Array.isArray(result)) return result.length if (isPlainObject(result)) return 1 + if (Array.isArray(result)) return result.length return result } diff --git a/lib/resolvers/crud/read.js b/lib/resolvers/crud/read.js index e1465068..a3c338df 100644 --- a/lib/resolvers/crud/read.js +++ b/lib/resolvers/crud/read.js @@ -28,7 +28,7 @@ module.exports = async (service, entity, selection) => { const result = await service.run(query) - const resultInArray = isPlainObject(result) && !Array.isArray(result) ? [result] : result + const resultInArray = isPlainObject(result) ? [result] : result return formatResult(selection, resultInArray) } diff --git a/lib/resolvers/crud/update.js b/lib/resolvers/crud/update.js index 6ebe6ebb..c861cc4f 100644 --- a/lib/resolvers/crud/update.js +++ b/lib/resolvers/crud/update.js @@ -37,7 +37,7 @@ module.exports = async (service, entity, selection) => { mergedResults = resultBeforeUpdate.map(original => ({ ...original, ...result })) } - const resultInArray = isPlainObject(mergedResults) && !Array.isArray(mergedResults) ? [mergedResults] : mergedResults + const resultInArray = isPlainObject(mergedResults) ? [mergedResults] : mergedResults return formatResult(selection, resultInArray, true) } diff --git a/lib/resolvers/utils/index.js b/lib/resolvers/utils/index.js index 36e1c586..5ea76237 100644 --- a/lib/resolvers/utils/index.js +++ b/lib/resolvers/utils/index.js @@ -1,4 +1,5 @@ -const isPlainObject = value => value !== null && typeof value === 'object' && !Buffer.isBuffer(value) +const isPlainObject = value => + value !== null && typeof value === 'object' && !Array.isArray(value) && !Buffer.isBuffer(value) const _ensureError = error => (error instanceof Error ? error : new Error(error)) From 3aa8900ee97f53176951d45541b14a1268484cf4 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 16:11:32 +0200 Subject: [PATCH 08/13] Improve comment --- lib/resolvers/crud/delete.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/resolvers/crud/delete.js b/lib/resolvers/crud/delete.js index fc239062..b24a80b7 100644 --- a/lib/resolvers/crud/delete.js +++ b/lib/resolvers/crud/delete.js @@ -17,7 +17,8 @@ module.exports = async (service, entity, selection) => { else throw e } - // Custom handlers can return non-numeric results for delete + // The CDS delete query returns the number of deleted entries + // However, custom handlers can return non-numeric results for delete if (isPlainObject(result)) return 1 if (Array.isArray(result)) return result.length From 38be8e23c5ae5dad3bb84aa837d446d1df76ecfe Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 17:53:26 +0200 Subject: [PATCH 09/13] Add changelog entries --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 132f0985..51e8eb77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Delete mutations now return the length of the array that is returned by a `DELETE` custom handler or 1 if a single object is returned + ### Changed +- Improved consistency of handling results of different types returned by custom handlers in CRUD resolvers + ### Fixed ### Removed From f849a3f4415b4e8da35ed177fd05a31ecd5b5a00 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 18:32:16 +0200 Subject: [PATCH 10/13] Improve changelog entries --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51e8eb77..c6b51d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Delete mutations now return the length of the array that is returned by a `DELETE` custom handler or 1 if a single object is returned - ### Changed -- Improved consistency of handling results of different types returned by custom handlers in CRUD resolvers +- Improved consistency of handling results of different types returned by custom handlers in CRUD resolvers: + + Wrap only objects (not primitive types) returned by custom handlers in arrays in Create, Update, and Delete resolvers + + Delete mutations now return the length of the array that is returned by a `DELETE` custom handler or 1 if a single object is returned ### Fixed From 72b1e454ab5cb01600df449c9a229ba689e26119 Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Mon, 3 Apr 2023 18:54:25 +0200 Subject: [PATCH 11/13] Return {} instead of [] for no update needed guard clause --- lib/resolvers/crud/update.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resolvers/crud/update.js b/lib/resolvers/crud/update.js index c861cc4f..90031aeb 100644 --- a/lib/resolvers/crud/update.js +++ b/lib/resolvers/crud/update.js @@ -27,7 +27,7 @@ module.exports = async (service, entity, selection) => { const result = await service.tx(async tx => { // read needs to be done before the update, otherwise the where clause might become invalid (case that properties in where clause are updated by the mutation) resultBeforeUpdate = await tx.run(queryBeforeUpdate) - if (resultBeforeUpdate.length === 0) return [] + if (resultBeforeUpdate.length === 0) return {} return tx.run(query) }) From 5595c70ff906a5c9be720d0f8ff0386527500a7a Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Tue, 4 Apr 2023 09:59:41 +0200 Subject: [PATCH 12/13] Changelog entry wording --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6b51d5a..d12d7409 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved consistency of handling results of different types returned by custom handlers in CRUD resolvers: - + Wrap only objects (not primitive types) returned by custom handlers in arrays in Create, Update, and Delete resolvers - + Delete mutations now return the length of the array that is returned by a `DELETE` custom handler or 1 if a single object is returned + + Wrap only objects (not primitive types) returned by custom handlers in arrays in create, update, and delete resolvers + + Delete mutations return the length of the array that is returned by a `DELETE` custom handler or 1 if a single object is returned ### Fixed From ba64049b9259a893a58a8705362435c7cf7781fd Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 5 Apr 2023 10:15:33 +0200 Subject: [PATCH 13/13] Fix and improve changelog entries --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d12d7409..824578d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved consistency of handling results of different types returned by custom handlers in CRUD resolvers: - + Wrap only objects (not primitive types) returned by custom handlers in arrays in create, update, and delete resolvers - + Delete mutations return the length of the array that is returned by a `DELETE` custom handler or 1 if a single object is returned + + Wrap only objects (i.e. not primitive types or arrays) returned by custom handlers in arrays in create, read, and update resolvers + + Delete mutations return the length of an array that is returned by a `DELETE` custom handler or 1 if a single object is returned ### Fixed