From d848d39d8c7ffd08fcd3df8d6953202a88f68855 Mon Sep 17 00:00:00 2001 From: Nicolas Lunet Date: Wed, 25 Sep 2024 11:09:23 +0200 Subject: [PATCH] feat: etag on patch (#860) * feat: etag on patch * chore: etag * chore: fix snapshot --- .changeset/neat-spoons-do.md | 5 ++++ .../fe-mockserver-core/src/data/dataAccess.ts | 22 +++++++++++++---- .../src/router/batchRouter.ts | 3 --- .../__snapshots__/middleware.test.ts.snap | 24 +++++++++++++++++++ .../test/unit/middleware.test.ts | 19 +++++++++++++++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 .changeset/neat-spoons-do.md diff --git a/.changeset/neat-spoons-do.md b/.changeset/neat-spoons-do.md new file mode 100644 index 00000000..19f4b773 --- /dev/null +++ b/.changeset/neat-spoons-do.md @@ -0,0 +1,5 @@ +--- +'@sap-ux/fe-mockserver-core': patch +--- + +Etag on patch diff --git a/packages/fe-mockserver-core/src/data/dataAccess.ts b/packages/fe-mockserver-core/src/data/dataAccess.ts index 65bc2550..a5dc456e 100644 --- a/packages/fe-mockserver-core/src/data/dataAccess.ts +++ b/packages/fe-mockserver-core/src/data/dataAccess.ts @@ -944,13 +944,19 @@ export class DataAccess implements DataAccessInterface { } patchData = finalPatchObject; } - return (await this.getMockEntitySet(entitySetName)).performPATCH( + const mockEntitySet = await this.getMockEntitySet(entitySetName); + + const resultData = await mockEntitySet.performPATCH( odataRequest.queryPath[0].keys, patchData, odataRequest.tenantId, odataRequest, true ); + if (this.validateETag && !Array.isArray(resultData) && mockEntitySet.isDraft()) { + odataRequest.setETag(resultData['@odata.etag']); + } + return resultData; } public async createData(odataRequest: ODataRequest, postData: any) { @@ -1029,9 +1035,14 @@ export class DataAccess implements DataAccessInterface { currentKeys[key.name] = postData[key.name]; } }); - postData = await ( - await this.getMockEntitySet(parentEntitySet.name) - ).performPOST(currentKeys, postData, odataRequest.tenantId, odataRequest, true); + const mockEntitySet = await this.getMockEntitySet(parentEntitySet.name); + postData = await mockEntitySet.performPOST( + currentKeys, + postData, + odataRequest.tenantId, + odataRequest, + true + ); // Update keys from location parentEntitySet.entityType.keys.forEach((key) => { if (postData[key.name] !== undefined) { @@ -1057,6 +1068,9 @@ export class DataAccess implements DataAccessInterface { ); } odataRequest.setResponseData(await postData); + if (this.validateETag && !Array.isArray(postData) && mockEntitySet.isDraft()) { + odataRequest.setETag(postData['@odata.etag']); + } return postData; } else { throw new Error('Unknown Entity Set' + entitySetName); diff --git a/packages/fe-mockserver-core/src/router/batchRouter.ts b/packages/fe-mockserver-core/src/router/batchRouter.ts index c6aa71ab..ca8db2e3 100644 --- a/packages/fe-mockserver-core/src/router/batchRouter.ts +++ b/packages/fe-mockserver-core/src/router/batchRouter.ts @@ -103,9 +103,6 @@ function createBatchResponseObject( if (partRequest.getETag()) { batchResponse += `ETag: ${partRequest.getETag()}${NL}`; } - if (partRequest.getETag()) { - batchResponse += `ETag: ${partRequest.getETag()}${NL}`; - } batchResponse += NL; const responseData = partRequest.getResponseData(); batchResponse += `HTTP/1.1 ${partRequest.statusCode} ${http.STATUS_CODES[partRequest.statusCode]}${NL}`; diff --git a/packages/fe-mockserver-core/test/unit/__snapshots__/middleware.test.ts.snap b/packages/fe-mockserver-core/test/unit/__snapshots__/middleware.test.ts.snap index 503d9ab3..6f5933d7 100644 --- a/packages/fe-mockserver-core/test/unit/__snapshots__/middleware.test.ts.snap +++ b/packages/fe-mockserver-core/test/unit/__snapshots__/middleware.test.ts.snap @@ -709,6 +709,30 @@ exports[`V4 Requestor can update data through a call 4`] = ` } `; +exports[`V4 Requestor can update data through a call 5`] = ` +{ + "code": 412001, + "message": "ETag condition not met", +} +`; + +exports[`V4 Requestor can update data through a call 6`] = ` +{ + "@odata.context": "$metadata#RootElement(ID=556)/Prop1", + "@odata.metadataEtag": "W/"62b2-j3ePZjiQElXD9LZZ+gEAOLMD5nc"", + "HasActiveEntity": false, + "HasDraftEntity": false, + "ID": 556, + "IsActiveEntity": false, + "Prop1": "Lali", + "Prop2": "", + "Sibling_ID": 0, + "isBoundAction1Hidden": false, + "isBoundAction2Hidden": false, + "isBoundAction3Hidden": false, +} +`; + exports[`V4 Requestor get one data 1`] = ` { "@odata.context": "$metadata#RootElement(ID=2)", diff --git a/packages/fe-mockserver-core/test/unit/middleware.test.ts b/packages/fe-mockserver-core/test/unit/middleware.test.ts index 31f0fd37..4b7ec834 100644 --- a/packages/fe-mockserver-core/test/unit/middleware.test.ts +++ b/packages/fe-mockserver-core/test/unit/middleware.test.ts @@ -330,12 +330,31 @@ describe('V4 Requestor', function () { }) .execute(); delete res3.body.DraftAdministrativeData; + const newEtag = res3.body['@odata.etag']; delete res3.body['@odata.etag']; expect(res3.body).toMatchSnapshot(); const dataRes3 = await dataRequestor.getList('RootElement').executeAsBatch(); expect(dataRes3.length).toBe(5); expect(dataRes3[4].ID).toBe(556); expect(dataRes3[4].Prop1).toBe('Lali-hoho'); + const res4 = await dataRequestor + .updateData('RootElement(ID=556)/Prop1', 'Lali', true, 'PUT', { + 'If-Match': dataRes2[4]['@odata.etag'] + }) + .execute(); + expect(res4.body).toMatchSnapshot(); + const dataRes4 = await dataRequestor.getList('RootElement').executeAsBatch(); + expect(dataRes4.length).toBe(5); + expect(dataRes4[4].ID).toBe(556); + expect(dataRes4[4].Prop1).toBe('Lali-hoho'); + const res5 = await dataRequestor + .updateData('RootElement(ID=556)/Prop1', 'Lali', true, 'PUT', { + 'If-Match': newEtag + }) + .execute(); + delete res5.body['@odata.etag']; + delete res5.body.DraftAdministrativeData; + expect(res5.body).toMatchSnapshot(); }); describe('Sticky', () => { const dataRequestor = new ODataV4Requestor('http://localhost:33331/tenant-0/sap/fe/core/mock/sticky');