diff --git a/.changeset/proud-buses-repeat.md b/.changeset/proud-buses-repeat.md new file mode 100644 index 0000000000..c62fedd1ad --- /dev/null +++ b/.changeset/proud-buses-repeat.md @@ -0,0 +1,8 @@ +--- +'@sap-ux/mockserver-config-writer': minor +'@sap-ux/odata-service-writer': minor +'@sap-ux/ui5-config': minor +'@sap-ux/create': minor +--- + +Support for multiple services and multiple annotations per service. diff --git a/packages/create/src/cli/add/mockserver-config.ts b/packages/create/src/cli/add/mockserver-config.ts index 0dcb0cf0c5..cd26bea429 100644 --- a/packages/create/src/cli/add/mockserver-config.ts +++ b/packages/create/src/cli/add/mockserver-config.ts @@ -55,7 +55,8 @@ async function addMockserverConfig( const webappPath = await getWebappPath(basePath); const config: MockserverConfig = { webappPath }; if (interactive) { - const questions = getMockserverConfigQuestions({ webappPath }); + const questions = getMockserverConfigQuestions({ webappPath, askForOverwrite: true }); + // User responses for webappPath and whether to overwrite existing services in mockserver config config.ui5MockYamlConfig = await prompt(questions); } const fs = await generateMockserverConfig(basePath, config); diff --git a/packages/create/src/cli/remove/mockserver-config.ts b/packages/create/src/cli/remove/mockserver-config.ts index 5cbe0e5f3b..ec6548af6a 100644 --- a/packages/create/src/cli/remove/mockserver-config.ts +++ b/packages/create/src/cli/remove/mockserver-config.ts @@ -32,7 +32,7 @@ async function removeMockserverConfiguration(basePath: string, force: boolean): try { logger.debug(`Called remove mockserver-config for path '${basePath}', force is '${force}'`); await validateBasePath(basePath); - const fs = removeMockserverConfig(basePath); + const fs = await removeMockserverConfig(basePath); await traceChanges(fs); const hasDeletions = hasFileDeletes(fs); let doCommit = true; diff --git a/packages/create/test/unit/cli/add/mockserver-config.test.ts b/packages/create/test/unit/cli/add/mockserver-config.test.ts index 98dd3eb5f3..8ed06a04d2 100644 --- a/packages/create/test/unit/cli/add/mockserver-config.test.ts +++ b/packages/create/test/unit/cli/add/mockserver-config.test.ts @@ -105,7 +105,48 @@ describe('Test command add mockserver-config', () => { expect(logLevelSpy).not.toBeCalled(); expect(loggerMock.debug).toBeCalled(); expect(loggerMock.error).not.toBeCalled(); - expect(promptSpy).toBeCalledWith([{ webappPath: join(appRoot, 'webapp') }]); + expect(promptSpy).toBeCalledWith([{ webappPath: join(appRoot, 'webapp'), askForOverwrite: true }]); + expect(fsMock.commit).toBeCalled(); + expect(execNpmCommandSpy).toBeCalled(); + }); + + test('Test create-fiori add mockserver-config --interactive with overwrite option', async () => { + // Mock setup + jest.spyOn(mockserverWriter, 'getMockserverConfigQuestions').mockReturnValue([ + { + name: 'path', + type: 'text', + message: 'Path to mocked service' + }, + { + type: 'confirm', + name: 'overwrite', + message: 'Overwrite services' + } + ]); + const promptSpy = jest.spyOn(prompts, 'prompt'); + + // Test execution + const command = new Command('add'); + addAddMockserverConfigCommand(command); + await command.parseAsync(getArgv(['mockserver-config', appRoot, '--interactive'])); + + // Result check + expect(logLevelSpy).not.toBeCalled(); + expect(loggerMock.debug).toBeCalled(); + expect(loggerMock.error).not.toBeCalled(); + expect(promptSpy).toBeCalledWith([ + { + name: 'path', + type: 'text', + message: 'Path to mocked service' + }, + { + message: 'Overwrite services', + name: 'overwrite', + type: 'confirm' + } + ]); expect(fsMock.commit).toBeCalled(); expect(execNpmCommandSpy).toBeCalled(); }); diff --git a/packages/create/test/unit/cli/remove/mockserver-config.test.ts b/packages/create/test/unit/cli/remove/mockserver-config.test.ts index 5d8a0077a8..56ed563819 100644 --- a/packages/create/test/unit/cli/remove/mockserver-config.test.ts +++ b/packages/create/test/unit/cli/remove/mockserver-config.test.ts @@ -37,7 +37,7 @@ describe('Test command add mockserver-config', () => { dump: jest.fn(), commit: jest.fn().mockImplementation((callback) => callback()) } as Partial as Editor; - jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockReturnValue(fsMock); + jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockResolvedValue(fsMock); const promptSpy = jest.spyOn(prompts, 'prompt'); // Test execution @@ -61,7 +61,7 @@ describe('Test command add mockserver-config', () => { dump: jest.fn().mockReturnValue({ 'deleted.file': { state: 'deleted' } }), commit: jest.fn().mockImplementation((callback) => callback()) } as Partial as Editor; - jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockReturnValue(fsMock); + jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockResolvedValue(fsMock); const promptSpy = jest.spyOn(prompts, 'prompt').mockResolvedValue({ doCommit: false }); // Test execution @@ -85,7 +85,7 @@ describe('Test command add mockserver-config', () => { dump: jest.fn().mockReturnValue({ 'deleted.file': { state: 'deleted' } }), commit: jest.fn().mockImplementation((callback) => callback()) } as Partial as Editor; - jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockReturnValue(fsMock); + jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockResolvedValue(fsMock); jest.spyOn(prompts, 'prompt').mockResolvedValue({ doCommit: true }); // Test execution @@ -106,7 +106,7 @@ describe('Test command add mockserver-config', () => { dump: jest.fn().mockReturnValue({ 'deleted.file': { state: 'deleted' } }), commit: jest.fn().mockImplementation((callback) => callback()) } as Partial as Editor; - jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockReturnValue(fsMock); + jest.spyOn(mockserverWriter, 'removeMockserverConfig').mockResolvedValue(fsMock); const promptSpy = jest.spyOn(prompts, 'prompt'); // Test execution diff --git a/packages/fiori-elements-writer/test/__snapshots__/alp.test.ts.snap b/packages/fiori-elements-writer/test/__snapshots__/alp.test.ts.snap index e2f76400d8..32ce35ff08 100644 --- a/packages/fiori-elements-writer/test/__snapshots__/alp.test.ts.snap +++ b/packages/fiori-elements-writer/test/__snapshots__/alp.test.ts.snap @@ -92,10 +92,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_ALP_SO_ANA_SRV - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -137,11 +141,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_ALP_SO_ANA_SRV - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -269,7 +273,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -2337,7 +2341,7 @@ xmlns:edmx=\\"http://docs.oasis-open.org/odata/ns/edmx\\"> ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -5270,7 +5274,7 @@ xmlns:edmx=\\"http://docs.oasis-open.org/odata/ns/edmx\\"> \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -5278,7 +5282,7 @@ xmlns:edmx=\\"http://docs.oasis-open.org/odata/ns/edmx\\"> \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -6072,10 +6076,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/sap/c_salesordermanage_srv/srvd/sap/c_salesordermanage_sd_aggregate/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -6117,8 +6123,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/sap/c_salesordermanage_srv/srvd/sap/c_salesordermanage_sd_aggregate/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -6247,7 +6253,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -11484,7 +11490,7 @@ appDescription=A Fiori application.", \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, diff --git a/packages/fiori-elements-writer/test/__snapshots__/feop.test.ts.snap b/packages/fiori-elements-writer/test/__snapshots__/feop.test.ts.snap index 314dda3a42..281a031946 100644 --- a/packages/fiori-elements-writer/test/__snapshots__/feop.test.ts.snap +++ b/packages/fiori-elements-writer/test/__snapshots__/feop.test.ts.snap @@ -89,10 +89,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -134,8 +136,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -275,7 +277,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -3208,7 +3210,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -3816,10 +3818,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -3877,8 +3881,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -4041,7 +4045,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -6974,7 +6978,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, diff --git a/packages/fiori-elements-writer/test/__snapshots__/fpm.test.ts.snap b/packages/fiori-elements-writer/test/__snapshots__/fpm.test.ts.snap index 2538da2557..4b991422e5 100644 --- a/packages/fiori-elements-writer/test/__snapshots__/fpm.test.ts.snap +++ b/packages/fiori-elements-writer/test/__snapshots__/fpm.test.ts.snap @@ -90,10 +90,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -135,8 +137,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -325,7 +327,7 @@ MainTitle=Main", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -3258,7 +3260,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -3835,10 +3837,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -3896,8 +3900,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -4161,7 +4165,7 @@ MainTitle=Main", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -7094,7 +7098,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, diff --git a/packages/fiori-elements-writer/test/__snapshots__/lrop.test.ts.snap b/packages/fiori-elements-writer/test/__snapshots__/lrop.test.ts.snap index b6270d5cca..c72b352785 100644 --- a/packages/fiori-elements-writer/test/__snapshots__/lrop.test.ts.snap +++ b/packages/fiori-elements-writer/test/__snapshots__/lrop.test.ts.snap @@ -93,10 +93,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -139,11 +143,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -272,7 +276,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -1073,7 +1077,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -2910,7 +2914,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -2918,7 +2922,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -3719,10 +3723,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -3765,11 +3773,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -3898,7 +3906,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -4699,7 +4707,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -6536,7 +6544,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -6544,7 +6552,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -7400,10 +7408,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -7462,11 +7474,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -7627,7 +7639,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -8428,7 +8440,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -10265,7 +10277,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -10273,7 +10285,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -10842,10 +10854,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -10887,8 +10901,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -11017,7 +11031,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -13950,7 +13964,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -14527,10 +14541,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -14572,8 +14588,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -14702,7 +14718,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -17635,7 +17651,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -18213,10 +18229,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -18258,8 +18276,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -18388,7 +18406,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -21321,7 +21339,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -23797,10 +23815,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -23842,8 +23862,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -23972,7 +23992,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -24376,7 +24396,7 @@ xmlns:edmx=\\"http://docs.oasis-open.org/odata/ns/edmx\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -25145,10 +25165,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -25190,8 +25212,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -25320,7 +25342,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -28253,7 +28275,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -28830,10 +28852,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -28875,8 +28899,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -29005,7 +29029,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -31938,7 +31962,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -32520,10 +32544,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -32566,11 +32594,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -32699,7 +32727,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -33500,7 +33528,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -35337,7 +35365,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -35345,7 +35373,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -36148,10 +36176,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -36194,11 +36226,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -36327,7 +36359,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -37128,7 +37160,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -38965,7 +38997,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -38973,7 +39005,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -39532,10 +39564,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -39578,11 +39614,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -39711,7 +39747,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -40512,7 +40548,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -42350,7 +42386,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -42358,7 +42394,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -43159,10 +43195,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -43205,11 +43245,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -43338,7 +43378,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -44139,7 +44179,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -45977,7 +46017,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -45985,7 +46025,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -46841,10 +46881,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -46903,11 +46947,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -47068,7 +47112,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -47869,7 +47913,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -49706,7 +49750,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -49714,7 +49758,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -50331,10 +50375,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -50393,11 +50441,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -50558,7 +50606,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -51359,7 +51407,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -53196,7 +53244,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -53204,7 +53252,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -53821,10 +53869,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -53883,11 +53935,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -54048,7 +54100,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -54849,7 +54901,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -56686,7 +56738,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -56694,7 +56746,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -57707,10 +57759,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -57753,11 +57809,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -57848,7 +57904,7 @@ appTitle=App \\"Title\\" \\\\\\" appDescription=A Fiori application.", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -58649,7 +58705,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -60486,7 +60542,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -60494,7 +60550,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { diff --git a/packages/fiori-elements-writer/test/__snapshots__/ovp.test.ts.snap b/packages/fiori-elements-writer/test/__snapshots__/ovp.test.ts.snap index 64ae036f68..815926bf01 100644 --- a/packages/fiori-elements-writer/test/__snapshots__/ovp.test.ts.snap +++ b/packages/fiori-elements-writer/test/__snapshots__/ovp.test.ts.snap @@ -95,10 +95,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/GWSAMPLE_BASIC - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/GWSAMPLE_BASIC.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='GWSAMPLE_BASIC',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -140,11 +144,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/GWSAMPLE_BASIC - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/GWSAMPLE_BASIC.xml + - localPath: ./webapp/localService/mainService/GWSAMPLE_BASIC.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='GWSAMPLE_BASIC',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -272,7 +276,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/GWSAMPLE_BASIC.xml": Object { + "webapp/localService/mainService/GWSAMPLE_BASIC.xml": Object { "contents": " @@ -561,7 +565,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -1019,7 +1023,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> \\"GWSAMPLE_BASIC\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -1027,7 +1031,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='GWSAMPLE_BASIC',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/GWSAMPLE_BASIC.xml\\" + \\"localUri\\": \\"localService/mainService/GWSAMPLE_BASIC.xml\\" } }, \\"annotation\\": { @@ -1083,7 +1087,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> \\"bundleName\\": \\"feovp1.i18n.i18n\\" } }, - \\"mainModel\\": { + \\"\\": { \\"dataSource\\": \\"mainService\\", \\"preload\\": true, \\"settings\\": { @@ -1110,7 +1114,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> } }, \\"sap.ovp\\": { - \\"globalFilterModel\\": \\"mainModel\\", + \\"globalFilterModel\\": \\"\\", \\"globalFilterEntityType\\": \\"GlobalFilters\\", \\"containerLayout\\": \\"resizable\\", \\"enableLiveFilter\\": true, @@ -1846,10 +1850,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/GWSAMPLE_BASIC - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/GWSAMPLE_BASIC.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='GWSAMPLE_BASIC',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -1907,11 +1915,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/GWSAMPLE_BASIC - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/GWSAMPLE_BASIC.xml + - localPath: ./webapp/localService/mainService/GWSAMPLE_BASIC.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='GWSAMPLE_BASIC',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -2071,7 +2079,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/GWSAMPLE_BASIC.xml": Object { + "webapp/localService/mainService/GWSAMPLE_BASIC.xml": Object { "contents": " @@ -2360,7 +2368,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -2818,7 +2826,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> \\"GWSAMPLE_BASIC\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -2826,7 +2834,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='GWSAMPLE_BASIC',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/GWSAMPLE_BASIC.xml\\" + \\"localUri\\": \\"localService/mainService/GWSAMPLE_BASIC.xml\\" } }, \\"annotation\\": { @@ -2882,7 +2890,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> \\"bundleName\\": \\"feovp1.i18n.i18n\\" } }, - \\"mainModel\\": { + \\"\\": { \\"dataSource\\": \\"mainService\\", \\"preload\\": true, \\"settings\\": { @@ -2909,7 +2917,7 @@ xmlns:atom=\\"http://www.w3.org/2005/Atom\\"/> } }, \\"sap.ovp\\": { - \\"globalFilterModel\\": \\"mainModel\\", + \\"globalFilterModel\\": \\"\\", \\"globalFilterEntityType\\": \\"GlobalFilters\\", \\"containerLayout\\": \\"resizable\\", \\"enableLiveFilter\\": true, @@ -3357,10 +3365,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/sap/c_salesordermanage_srv/srvd/sap/c_salesordermanage_sd_aggregate/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -3402,8 +3412,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/sap/c_salesordermanage_srv/srvd/sap/c_salesordermanage_sd_aggregate/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -3532,7 +3542,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -8769,7 +8779,7 @@ appDescription=A Fiori application.", \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -8822,7 +8832,7 @@ appDescription=A Fiori application.", \\"bundleName\\": \\"feovp2.i18n.i18n\\" } }, - \\"mainModel\\": { + \\"\\": { \\"dataSource\\": \\"mainService\\", \\"preload\\": true, \\"settings\\": { @@ -8847,7 +8857,7 @@ appDescription=A Fiori application.", } }, \\"sap.ovp\\": { - \\"globalFilterModel\\": \\"mainModel\\", + \\"globalFilterModel\\": \\"\\", \\"globalFilterEntityType\\": \\"SalesOrderItem\\", \\"containerLayout\\": \\"resizable\\", \\"enableLiveFilter\\": true, @@ -9345,10 +9355,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/sap/c_salesordermanage_srv/srvd/sap/c_salesordermanage_sd_aggregate/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml builder: customTasks: - name: ui5-tooling-transpile-task @@ -9406,8 +9418,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/sap/c_salesordermanage_srv/srvd/sap/c_salesordermanage_sd_aggregate/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -9568,7 +9580,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -14805,7 +14817,7 @@ appDescription=A Fiori application.", \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -14858,7 +14870,7 @@ appDescription=A Fiori application.", \\"bundleName\\": \\"feovp2.i18n.i18n\\" } }, - \\"mainModel\\": { + \\"\\": { \\"dataSource\\": \\"mainService\\", \\"preload\\": true, \\"settings\\": { @@ -14883,7 +14895,7 @@ appDescription=A Fiori application.", } }, \\"sap.ovp\\": { - \\"globalFilterModel\\": \\"mainModel\\", + \\"globalFilterModel\\": \\"\\", \\"globalFilterEntityType\\": \\"SalesOrderItem\\", \\"containerLayout\\": \\"resizable\\", \\"enableLiveFilter\\": true, diff --git a/packages/fiori-elements-writer/test/__snapshots__/worklist.test.ts.snap b/packages/fiori-elements-writer/test/__snapshots__/worklist.test.ts.snap index a33045ef87..20569be8f6 100644 --- a/packages/fiori-elements-writer/test/__snapshots__/worklist.test.ts.snap +++ b/packages/fiori-elements-writer/test/__snapshots__/worklist.test.ts.snap @@ -94,10 +94,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -140,11 +144,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -273,7 +277,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -1074,7 +1078,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -2911,7 +2915,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -2919,7 +2923,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -3728,10 +3732,14 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -3774,11 +3782,11 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml + - localPath: ./webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/ - localPath: ./webapp/annotations/annotation.xml urlPath: annotations/annotation.xml @@ -3907,7 +3915,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { + "webapp/localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml": Object { "contents": " @@ -4708,7 +4716,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -6545,7 +6553,7 @@ appDescription=A Fiori application.", \\"SEPMRA_PROD_MAN_ANNO_MDL\\", \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -6553,7 +6561,7 @@ appDescription=A Fiori application.", \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN_ANNO_MDL',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" + \\"localUri\\": \\"localService/mainService/SEPMRA_PROD_MAN_ANNO_MDL.xml\\" } }, \\"annotation\\": { @@ -7357,10 +7365,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -7402,8 +7412,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -7532,7 +7542,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -10465,7 +10475,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -11044,10 +11054,12 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml ", "state": "modified", }, @@ -11089,8 +11101,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata4/dmo/sb_travel_mduu_o4/srvd/dmo/sd_travel_mduu/0001 - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotation.xml @@ -11219,7 +11231,7 @@ appDescription=A Fiori application.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -14152,7 +14164,7 @@ EntityType=\\"com.sap.gateway.srvd.dmo.sd_travel_mduu.v0001.TravelAgencyType\\"> \\"annotations\\": [ \\"annotation\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, diff --git a/packages/fiori-freestyle-writer/test/__snapshots__/basic.test.ts.snap b/packages/fiori-freestyle-writer/test/__snapshots__/basic.test.ts.snap index d55e69460b..4bfe7c558d 100644 --- a/packages/fiori-freestyle-writer/test/__snapshots__/basic.test.ts.snap +++ b/packages/fiori-freestyle-writer/test/__snapshots__/basic.test.ts.snap @@ -2279,8 +2279,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -2340,8 +2340,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -2523,7 +2523,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -2550,7 +2550,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -2917,8 +2917,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -2978,8 +2978,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -3161,7 +3161,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -3188,7 +3188,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -3549,8 +3549,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -3610,8 +3610,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -3793,7 +3793,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -3820,7 +3820,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -4181,8 +4181,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -4242,8 +4242,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -4423,7 +4423,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -4450,7 +4450,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -4749,8 +4749,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -4794,8 +4794,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -4947,7 +4947,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -4974,7 +4974,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -5298,8 +5298,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -5343,8 +5343,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -5496,7 +5496,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -5523,7 +5523,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -6110,8 +6110,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -6155,8 +6155,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -6324,7 +6324,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -6351,7 +6351,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -6924,8 +6924,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -6969,8 +6969,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -7138,7 +7138,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -7165,7 +7165,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -7738,8 +7738,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -7783,8 +7783,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -7952,7 +7952,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -7980,7 +7980,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -8553,8 +8553,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -8598,8 +8598,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -8767,7 +8767,7 @@ title=App Title", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -8794,7 +8794,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -9122,8 +9122,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -9167,8 +9167,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -9298,7 +9298,7 @@ appDescription=A Fiori application. title=App Title", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": "", "state": "modified", }, @@ -9325,7 +9325,7 @@ title=App Title", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } diff --git a/packages/fiori-freestyle-writer/test/__snapshots__/index.test.ts.snap b/packages/fiori-freestyle-writer/test/__snapshots__/index.test.ts.snap index 5aa0cb3126..28d7cb3ce6 100644 --- a/packages/fiori-freestyle-writer/test/__snapshots__/index.test.ts.snap +++ b/packages/fiori-freestyle-writer/test/__snapshots__/index.test.ts.snap @@ -87,8 +87,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -132,8 +132,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -301,7 +301,7 @@ title=My Test App", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -884,7 +884,7 @@ title=My Test App", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -1203,8 +1203,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -1248,8 +1248,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -2411,7 +2411,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -2994,7 +2994,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -3879,8 +3879,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -3924,8 +3924,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -4627,7 +4627,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -5210,7 +5210,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } diff --git a/packages/fiori-freestyle-writer/test/__snapshots__/listdetail.test.ts.snap b/packages/fiori-freestyle-writer/test/__snapshots__/listdetail.test.ts.snap index ae767aac11..1b72ef0f27 100644 --- a/packages/fiori-freestyle-writer/test/__snapshots__/listdetail.test.ts.snap +++ b/packages/fiori-freestyle-writer/test/__snapshots__/listdetail.test.ts.snap @@ -142,8 +142,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -203,8 +203,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -1351,7 +1351,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -1934,7 +1934,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -2860,8 +2860,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -2921,8 +2921,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -4069,7 +4069,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -4652,7 +4652,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -5578,8 +5578,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -5639,8 +5639,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -6787,7 +6787,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -7370,7 +7370,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -8274,8 +8274,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -8319,8 +8319,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -9402,7 +9402,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -9985,7 +9985,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -10872,8 +10872,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -10933,8 +10933,8 @@ server: mountPath: / services: - urlPath: /V2/Northwind/Northwind.svc - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -11992,7 +11992,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -12575,7 +12575,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } diff --git a/packages/fiori-freestyle-writer/test/__snapshots__/worklist.test.ts.snap b/packages/fiori-freestyle-writer/test/__snapshots__/worklist.test.ts.snap index 14763677b2..65e736462d 100644 --- a/packages/fiori-freestyle-writer/test/__snapshots__/worklist.test.ts.snap +++ b/packages/fiori-freestyle-writer/test/__snapshots__/worklist.test.ts.snap @@ -92,8 +92,8 @@ server: mountPath: / services: - urlPath: /here/goes/your/serviceurl - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -137,8 +137,8 @@ server: mountPath: / services: - urlPath: /here/goes/your/serviceurl - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -834,7 +834,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -6078,7 +6078,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } } @@ -6858,8 +6858,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -6906,8 +6906,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -7612,7 +7612,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -9446,7 +9446,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -10228,8 +10228,8 @@ server: mountPath: / services: - urlPath: /catalog-admin-noauth - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -10273,8 +10273,8 @@ server: mountPath: / services: - urlPath: /catalog-admin-noauth - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] ", @@ -10970,7 +10970,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -12395,7 +12395,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } } @@ -13230,8 +13230,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -13294,8 +13294,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -13974,7 +13974,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -15808,7 +15808,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } @@ -16624,8 +16624,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -16688,8 +16688,8 @@ server: mountPath: / services: - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: [] builder: @@ -17368,7 +17368,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -19202,7 +19202,7 @@ errorText=Sorry, a technical error occurred! Please try again later.", \\"type\\": \\"OData\\", \\"settings\\": { \\"annotations\\": [], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } } diff --git a/packages/mockserver-config-writer/src/app-info.ts b/packages/mockserver-config-writer/src/app-info.ts index b79f95dc8f..c28ee6f87c 100644 --- a/packages/mockserver-config-writer/src/app-info.ts +++ b/packages/mockserver-config-writer/src/app-info.ts @@ -1,5 +1,16 @@ import type { Manifest, ManifestNamespace } from '@sap-ux/project-access'; +/** + * Get the main service data source name from manifest.json. + * + * @param manifest - Parsed manifest.json + * @returns - data source name from manifest.json + */ +function getMainServiceDataSourceName(manifest: Manifest): string | undefined { + const modelName = manifest['sap.ovp']?.globalFilterModel ?? ''; + return manifest['sap.ui5']?.models?.[modelName]?.dataSource; +} + /** * Get the main service data source entry from manifest.json. * @@ -8,11 +19,7 @@ import type { Manifest, ManifestNamespace } from '@sap-ux/project-access'; */ export function getMainServiceDataSource(manifest: Manifest): ManifestNamespace.DataSource | undefined { let dataSource; - const model = manifest['sap.ovp']?.globalFilterModel || ''; - const dataSourceName = - manifest['sap.ui5'] && manifest['sap.ui5'].models?.[model] - ? manifest['sap.ui5'].models[model].dataSource - : undefined; + const dataSourceName = getMainServiceDataSourceName(manifest); if (dataSourceName) { dataSource = manifest['sap.app'].dataSources?.[dataSourceName]; } @@ -31,7 +38,7 @@ export function getODataSources( dataSourceType: ManifestNamespace.DataSourceEnum['type'] = 'OData' ): { [k: string]: ManifestNamespace.DataSource } { const result: { [k: string]: ManifestNamespace.DataSource } = {}; - const dataSources = manifest['sap.app']?.dataSources || {}; + const dataSources = manifest['sap.app']?.dataSources ?? {}; for (const dataSource in dataSources) { if (dataSources[dataSource].uri && dataSources[dataSource].type === dataSourceType) { result[dataSource] = dataSources[dataSource]; diff --git a/packages/mockserver-config-writer/src/i18n.ts b/packages/mockserver-config-writer/src/i18n.ts index 9c48a6cce7..8d11134d77 100644 --- a/packages/mockserver-config-writer/src/i18n.ts +++ b/packages/mockserver-config-writer/src/i18n.ts @@ -1,14 +1,15 @@ -import type { TOptions } from 'i18next'; +import type { i18n, TOptions } from 'i18next'; import i18next from 'i18next'; import translations from './translations/mockserver-config-writer.i18n.json'; -const NS = 'odata-service-writer'; +const NS = 'mockserver-config-writer'; +let i18nInstance: i18n; /** * Initialize i18next with the translations for this module. */ export async function initI18n(): Promise { - await i18next.init({ + i18nInstance = i18next.createInstance({ resources: { en: { [NS]: translations @@ -19,6 +20,7 @@ export async function initI18n(): Promise { defaultNS: NS, ns: [NS] }); + await i18nInstance.init(); } /** @@ -29,7 +31,7 @@ export async function initI18n(): Promise { * @returns {string} localized string stored for the given key */ export function t(key: string, options?: TOptions): string { - return i18next.t(key, options); + return i18nInstance.t(key, options); } initI18n().catch(() => { diff --git a/packages/mockserver-config-writer/src/mockserver-config/index.ts b/packages/mockserver-config-writer/src/mockserver-config/index.ts index af735d6352..dd540d630b 100644 --- a/packages/mockserver-config-writer/src/mockserver-config/index.ts +++ b/packages/mockserver-config-writer/src/mockserver-config/index.ts @@ -3,7 +3,7 @@ import { create } from 'mem-fs-editor'; import type { Editor } from 'mem-fs-editor'; import type { MockserverConfig } from '../types'; import { enhancePackageJson, removeFromPackageJson } from './package-json'; -import { enhanceYaml, removeUi5MockYaml } from './ui5-mock-yaml'; +import { enhanceYaml, removeMockDataFolders, removeUi5MockYaml } from './ui5-mock-yaml'; /** * Add mockserver configuration to a UI5 application. @@ -29,11 +29,12 @@ export async function generateMockserverConfig(basePath: string, data: Mockserve * @param fs - the memfs editor instance * @returns Promise - memfs editor instance with updated files */ -export function removeMockserverConfig(basePath: string, fs?: Editor): Editor { +export async function removeMockserverConfig(basePath: string, fs?: Editor): Promise { if (!fs) { fs = create(createStorage()); } removeFromPackageJson(fs, basePath); removeUi5MockYaml(fs, basePath); + await removeMockDataFolders(fs, basePath); return fs; } diff --git a/packages/mockserver-config-writer/src/mockserver-config/package-json.ts b/packages/mockserver-config-writer/src/mockserver-config/package-json.ts index afe2334aac..36cce46610 100644 --- a/packages/mockserver-config-writer/src/mockserver-config/package-json.ts +++ b/packages/mockserver-config-writer/src/mockserver-config/package-json.ts @@ -30,8 +30,8 @@ function enhanceDependencies( packageJson: Package, mockserverModule = '@sap-ux/ui5-middleware-fe-mockserver', version = '2' -) { - packageJson.devDependencies = packageJson.devDependencies || {}; +): void { + packageJson.devDependencies = packageJson.devDependencies ?? {}; delete packageJson.devDependencies['@sap/ux-ui5-fe-mockserver-middleware']; packageJson.devDependencies[mockserverModule] = version; if (isUi5CliHigherTwo(packageJson.devDependencies)) { @@ -135,7 +135,7 @@ function replaceConfig(startScript: string, configStartIndex: number): string { * * @param packageJson - parsed package.json content */ -function removeMockserverUi5Dependencies(packageJson: Package) { +function removeMockserverUi5Dependencies(packageJson: Package): void { const removeModules = new Set(['@sap/ux-ui5-fe-mockserver-middleware', '@sap-ux/ui5-middleware-fe-mockserver']); if (packageJson.ui5?.dependencies && Array.isArray(packageJson.ui5.dependencies)) { packageJson.ui5.dependencies = packageJson.ui5.dependencies.filter((d) => !removeModules.has(d)); diff --git a/packages/mockserver-config-writer/src/mockserver-config/ui5-mock-yaml.ts b/packages/mockserver-config-writer/src/mockserver-config/ui5-mock-yaml.ts index 0edd8bd688..811c9d6f3c 100644 --- a/packages/mockserver-config-writer/src/mockserver-config/ui5-mock-yaml.ts +++ b/packages/mockserver-config-writer/src/mockserver-config/ui5-mock-yaml.ts @@ -1,14 +1,17 @@ import { join } from 'path'; import type { Editor } from 'mem-fs-editor'; import { UI5Config } from '@sap-ux/ui5-config'; -import type { CustomMiddleware } from '@sap-ux/ui5-config'; +import type { CustomMiddleware, DataSourceConfig } from '@sap-ux/ui5-config'; import type { Manifest } from '@sap-ux/project-access'; +import { DirName, FileName, getWebappPath, readUi5Yaml } from '@sap-ux/project-access'; import type { Ui5MockYamlConfig } from '../types'; import type { MockserverConfig } from '@sap-ux/ui5-config/dist/types'; -import { getMainServiceDataSource, getODataSources } from '../app-info'; +import { getODataSources } from '../app-info'; /** * Enhance or create the ui5-mock.yaml with mockserver config. + * Mockserver config services and annotations are collected from associated manifest.json file of the project. + * If there aren't any services or annotations defined in manifest dataSources section, then mockserver config will be generated without those. * Following enhancement strategy is applied: * * ui5-mock.yaml exists @@ -33,45 +36,100 @@ export async function enhanceYaml( webappPath: string, config?: Ui5MockYamlConfig ): Promise { + const overwrite = !!config?.overwrite; const ui5MockYamlPath = join(basePath, 'ui5-mock.yaml'); let mockConfig; const manifest = fs.readJSON(join(webappPath, 'manifest.json')) as Partial as Manifest; - const mockserverPath = config?.path || getMainServiceDataSource(manifest)?.uri; + // Prepare annotations list to be used in mockserver middleware config annotations const annotationSource = Object.values(getODataSources(manifest, 'ODataAnnotation')); const annotationsConfig = annotationSource.map((annotation) => ({ localPath: `./webapp/${annotation.settings?.localUri}`, urlPath: annotation.uri })); + // Prepare dataSources list to be used in mockserver middleware config services + const dataSources = getODataSources(manifest); + const dataSourcesConfig: DataSourceConfig[] = []; + for (const dataSource in dataSources) { + const localUri = dataSources[dataSource].settings?.localUri; + dataSourcesConfig.push({ + serviceName: dataSource, + servicePath: dataSources[dataSource].uri, + metadataPath: localUri ? `./webapp/${localUri}` : undefined + }); + } if (fs.exists(ui5MockYamlPath)) { - mockConfig = await updateUi5MockYamlConfig(fs, ui5MockYamlPath, mockserverPath, annotationsConfig); + mockConfig = await updateUi5MockYamlConfig( + fs, + ui5MockYamlPath, + dataSourcesConfig, + annotationsConfig, + overwrite + ); } else { mockConfig = fs.exists(join(basePath, 'ui5.yaml')) - ? await generateUi5MockYamlBasedOnUi5Yaml(fs, basePath, mockserverPath, annotationsConfig) - : await generateNewUi5MockYamlConfig(manifest['sap.app']?.id || '', mockserverPath, annotationsConfig); + ? await generateUi5MockYamlBasedOnUi5Yaml(fs, basePath, dataSourcesConfig, annotationsConfig) + : await generateNewUi5MockYamlConfig(manifest['sap.app']?.id || '', dataSourcesConfig, annotationsConfig); } const yaml = mockConfig.toString(); fs.write(ui5MockYamlPath, yaml); } /** - * Update existing ui5-mock.yaml config. This will add or replace existing middleware configuration + * Deletes mock data folders for all services from mem-fs. + * + * @param fs - mem-fs reference to be used for file access + * @param basePath - path to project root, where package.json and ui5.yaml is + */ +export async function removeMockDataFolders(fs: Editor, basePath: string): Promise { + const webappPath = await getWebappPath(basePath, fs); + const manifestPath = join(webappPath, FileName.Manifest); + const manifest = fs.readJSON(manifestPath) as unknown as Manifest; + // Read service names from manifest.json + const dataSources = manifest['sap.app'].dataSources; + if (dataSources) { + const serviceNames = Object.keys(dataSources); + serviceNames.forEach((serviceName: string) => { + const mockdataPath = join(webappPath, DirName.LocalService, serviceName, DirName.Data); + if (mockdataPath) { + fs.delete(mockdataPath); + } + }); + } +} + +/** + * Update existing ui5-mock.yaml config. This will add or replace existing middleware configuration. + * If 'overwrite' is set to true, then mockserver middleware configuration would be replaced else only enhanced with data from 'name' and 'path'. * 'sap-fe-mockserver' with state of the art config. * * @param fs - Editor instance to read existing information * @param ui5MockYamlPath - path to ui5-mock.yaml file - * @param [path] - optional url path the mockserver listens to - * @param annotationsConfig - optional annotations config to add to mockserver middleware - * @returns {*} {Promise} - Update Yaml Doc + * @param dataSourcesConfig - dataSources config from manifest to add to mockserver middleware services list + * @param annotationsConfig - annotations config to add to mockserver mockserver middleware annotations list + * @param overwrite - optional, whether to overwrite existing annotations and services + * @returns {*} {Promise} - Updated Yaml Doc */ async function updateUi5MockYamlConfig( fs: Editor, ui5MockYamlPath: string, - path?: string, - annotationsConfig?: MockserverConfig['annotations'] + dataSourcesConfig: DataSourceConfig[], + annotationsConfig: MockserverConfig['annotations'], + overwrite = false ): Promise { const existingUi5MockYamlConfig = await UI5Config.newInstance(fs.read(ui5MockYamlPath)); - existingUi5MockYamlConfig.updateCustomMiddleware(await getNewMockserverMiddleware(path, annotationsConfig)); + if (overwrite) { + const newMockserverMiddleware = await getNewMockserverMiddleware(dataSourcesConfig, annotationsConfig); + existingUi5MockYamlConfig.updateCustomMiddleware(newMockserverMiddleware); + } else { + for (const dataSourceName in dataSourcesConfig) { + existingUi5MockYamlConfig.addServiceToMockserverMiddleware( + dataSourcesConfig[dataSourceName], + undefined, + annotationsConfig + ); + } + } return existingUi5MockYamlConfig; } @@ -79,19 +137,20 @@ async function updateUi5MockYamlConfig( * Create a new ui5-mock.yaml based on existing ui5.yaml. * * @param fs - Editor instance to read existing information - * @param basePath - - * @param [path] - optional path for mockserver config - * @param annotationsConfig - optional annotations config to add to mockserver middleware + * @param basePath - the base path where the package.json and ui5.yaml is + * @param dataSourcesConfig - dataSources config from manifest to add to mockserver middleware services list + * @param annotationsConfig - annotations config to add to mockserver mockserver middleware annotations list * @returns {*} {Promise} - Update Yaml Doc */ async function generateUi5MockYamlBasedOnUi5Yaml( fs: Editor, basePath: string, - path?: string, - annotationsConfig?: MockserverConfig['annotations'] + dataSourcesConfig: DataSourceConfig[], + annotationsConfig: MockserverConfig['annotations'] ): Promise { - const ui5MockYamlConfig = await UI5Config.newInstance(fs.read(join(basePath, 'ui5.yaml'))); - ui5MockYamlConfig.updateCustomMiddleware(await getNewMockserverMiddleware(path, annotationsConfig)); + const ui5MockYamlConfig = await readUi5Yaml(basePath, FileName.Ui5Yaml, fs); + const ui5MockServerMiddleware = await getNewMockserverMiddleware(dataSourcesConfig, annotationsConfig); + ui5MockYamlConfig.updateCustomMiddleware(ui5MockServerMiddleware); return ui5MockYamlConfig; } @@ -99,14 +158,14 @@ async function generateUi5MockYamlBasedOnUi5Yaml( * Create fresh ui5-mock.yaml configuration which can be stringified and written. * * @param appId - application id - * @param [path] - optional url path the mockserver listens to - * @param annotationsConfig - optional annotations config to add to mockserver middleware + * @param dataSourcesConfig - dataSources config from manifest to add to mockserver middleware services list + * @param annotationsConfig - annotations config to add to mockserver mockserver middleware annotations list * @returns {*} {Promise} - Update Yaml Doc */ async function generateNewUi5MockYamlConfig( appId: string, - path?: string, - annotationsConfig?: MockserverConfig['annotations'] + dataSourcesConfig: DataSourceConfig[], + annotationsConfig: MockserverConfig['annotations'] ): Promise { const ui5MockYaml = await UI5Config.newInstance( '# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json\n\nspecVersion: "2.5"' @@ -115,23 +174,23 @@ async function generateNewUi5MockYamlConfig( ui5MockYaml.setType('application'); ui5MockYaml.addFioriToolsProxydMiddleware({ ui5: {} }); ui5MockYaml.addFioriToolsAppReloadMiddleware(); - ui5MockYaml.addMockServerMiddleware(path, annotationsConfig); + ui5MockYaml.addMockServerMiddleware(dataSourcesConfig, annotationsConfig); return ui5MockYaml; } /** * Return new mockserver middleware. * - * @param [path] - optional path for mockserver config - * @param annotationsConfig - optional annotations config to add to mockserver middleware + * @param dataSourcesConfig - dataSources config from manifest to add to mockserver middleware services list + * @param annotationsConfig - annotations config to add to mockserver mockserver middleware annotations list * @returns - mockserver middleware */ async function getNewMockserverMiddleware( - path?: string, - annotationsConfig?: MockserverConfig['annotations'] + dataSourcesConfig: DataSourceConfig[], + annotationsConfig: MockserverConfig['annotations'] ): Promise> { const ui5MockYaml = await UI5Config.newInstance(''); - ui5MockYaml.addMockServerMiddleware(path, annotationsConfig); + ui5MockYaml.addMockServerMiddleware(dataSourcesConfig, annotationsConfig); const mockserverMiddleware = ui5MockYaml.findCustomMiddleware('sap-fe-mockserver'); if (!mockserverMiddleware) { throw Error('Could not create new mockserver config'); diff --git a/packages/mockserver-config-writer/src/prompt/index.ts b/packages/mockserver-config-writer/src/prompt/index.ts index f1daf6432e..357c2d2194 100644 --- a/packages/mockserver-config-writer/src/prompt/index.ts +++ b/packages/mockserver-config-writer/src/prompt/index.ts @@ -12,33 +12,48 @@ import { t } from '..'; * * @param params - optional parameters used to fill default values * @param params.webappPath - optional path to webapp folder, where manifest is + * @param params.askForOverwrite - optional, whether to overwrite services in mockserver config * @param params.fs - optional memfs editor instance * @returns - array of questions that serves as input for prompt module */ -export function getMockserverConfigQuestions(params?: { webappPath?: string; fs?: Editor }): PromptObject[] { - const question: Partial = { +export function getMockserverConfigQuestions(params?: { + webappPath?: string; + askForOverwrite?: boolean; + fs?: Editor; +}): PromptObject[] { + const prompts: PromptObject[] = []; + const questionPath: Partial = { name: 'path', message: t('questions.pathToMock') }; if (params?.webappPath) { - const fs = params.fs || create(createStorage()); + const fs = params.fs ?? create(createStorage()); const manifest: Manifest = JSON.parse(fs.read(join(params.webappPath, 'manifest.json'))); - const mainDataSourceUri = getMainServiceDataSource(manifest)?.uri || ''; + const mainDataSourceUri = getMainServiceDataSource(manifest)?.uri ?? ''; const oDataSources = getODataSources(manifest); const choices: Choice[] = []; for (const dsName in oDataSources) { choices.push({ title: `${dsName}: ${oDataSources[dsName].uri}`, value: oDataSources[dsName].uri, - description: oDataSources[dsName].settings?.odataVersion || undefined + description: oDataSources[dsName].settings?.odataVersion ?? undefined }); } if (choices.length > 0) { - question.type = 'select'; - question.choices = choices; - question.initial = choices.findIndex((c) => c.value === mainDataSourceUri); + questionPath.type = 'select'; + questionPath.choices = choices; + questionPath.initial = choices.findIndex((c) => c.value === mainDataSourceUri); } } - question.type ||= 'text'; - return [question as PromptObject]; + questionPath.type ||= 'text'; + prompts.push(questionPath as PromptObject); + if (params?.askForOverwrite) { + const questionOverwrite: Partial = { + type: 'confirm', + name: 'overwrite', + message: t('questions.overwrite') + }; + prompts.push(questionOverwrite as PromptObject); + } + return prompts; } diff --git a/packages/mockserver-config-writer/src/translations/mockserver-config-writer.i18n.json b/packages/mockserver-config-writer/src/translations/mockserver-config-writer.i18n.json index 942544770a..2b303bda37 100644 --- a/packages/mockserver-config-writer/src/translations/mockserver-config-writer.i18n.json +++ b/packages/mockserver-config-writer/src/translations/mockserver-config-writer.i18n.json @@ -1,5 +1,6 @@ { "questions": { - "pathToMock": "Path to mocked service" + "pathToMock": "Path to mocked service", + "overwrite": "Overwrite services" } } diff --git a/packages/mockserver-config-writer/src/types/index.ts b/packages/mockserver-config-writer/src/types/index.ts index fcd38de6da..7260eacf51 100644 --- a/packages/mockserver-config-writer/src/types/index.ts +++ b/packages/mockserver-config-writer/src/types/index.ts @@ -11,5 +11,5 @@ export interface PackageJsonMockConfig { } export interface Ui5MockYamlConfig { - path?: string; + overwrite?: boolean; } diff --git a/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/package.json b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/package.json new file mode 100644 index 0000000000..4e974d71b6 --- /dev/null +++ b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/package.json @@ -0,0 +1,4 @@ +{ + "name": "ui5-mock-config" +} + \ No newline at end of file diff --git a/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/ui5-mock.yaml b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/ui5-mock.yaml new file mode 100644 index 0000000000..990d744376 --- /dev/null +++ b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/ui5-mock.yaml @@ -0,0 +1,18 @@ +specVersion: "2.6" +metadata: + name: ui-mock-config +type: application +server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: + - urlPath: /first/path + mockdataPath: ./webapp/localService/mainService/data + generateMockData: true + - urlPath: /second/path + mockdataPath: ./webapp/localService/STTA_SALES_ORDER_ND_SRV_01/data + generateMockData: true + annotations: [] diff --git a/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/localService/STTA_SALES_ORDER_ND_SRV_01/data/keep b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/localService/STTA_SALES_ORDER_ND_SRV_01/data/keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/localService/mainService/data/keep b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/localService/mainService/data/keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/manifest.json b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/manifest.json new file mode 100644 index 0000000000..29fb800bd3 --- /dev/null +++ b/packages/mockserver-config-writer/test/fixtures/ui5-mock-config/webapp/manifest.json @@ -0,0 +1,20 @@ +{ + "_version": "1.49.0", + "sap.app": { + "id": "ui5-mock-config", + "type": "application", + "dataSources": { + "mainService": { + "uri": "/sap/opu/odata/sap/SEPMRA_PROD_MAN/", + "type": "OData", + "settings": {} + }, + "STTA_SALES_ORDER_ND_SRV_01": { + "uri": "/sap/opu/odata/sap/STTA_SALES_ORDER_ND_SRV_01/", + "type": "OData", + "settings": {} + } + } + } + } + \ No newline at end of file diff --git a/packages/mockserver-config-writer/test/unit/app-info.test.ts b/packages/mockserver-config-writer/test/unit/app-info.test.ts index 8574ed6197..1fd4d9b4d5 100644 --- a/packages/mockserver-config-writer/test/unit/app-info.test.ts +++ b/packages/mockserver-config-writer/test/unit/app-info.test.ts @@ -1,5 +1,5 @@ import type { Manifest } from '@sap-ux/project-access'; -import { getMainServiceDataSource, getODataSources } from '../../src/app-info'; +import { getODataSources, getMainServiceDataSource } from '../../src/app-info'; describe('Tests for getMainServiceDataSource()', () => { test('Empty manifest.json, should return undefined', () => { diff --git a/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/index.test.ts.snap b/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/index.test.ts.snap index 7e738ae6ca..7c2587fb74 100644 --- a/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/index.test.ts.snap +++ b/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/index.test.ts.snap @@ -1,6 +1,36 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Test generateMockserverConfig() Add config to bare minimum project 1`] = ` +exports[`Test generateMockserverConfig() Add config with services to project 1`] = ` +"specVersion: \\"2.6\\" +metadata: + name: ui-mock-config +type: application +server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: + - urlPath: /first/path + mockdataPath: ./webapp/localService/mainService/data + generateMockData: true + - urlPath: /second/path + mockdataPath: ./webapp/localService/STTA_SALES_ORDER_ND_SRV_01/data + generateMockData: true + - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data + generateMockData: true + - urlPath: /sap/opu/odata/sap/STTA_SALES_ORDER_ND_SRV_01 + metadataPath: ./webapp/localService/STTA_SALES_ORDER_ND_SRV_01/metadata.xml + mockdataPath: ./webapp/localService/STTA_SALES_ORDER_ND_SRV_01/data + generateMockData: true + annotations: [] +" +`; + +exports[`Test generateMockserverConfig() Add config without any services to bare minimum project 1`] = ` "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json specVersion: \\"2.5\\" @@ -28,11 +58,7 @@ server: beforeMiddleware: csp configuration: mountPath: / - services: - - urlPath: '' - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data - generateMockData: true + services: [] annotations: [] " `; diff --git a/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/ui5-mock-yaml.test.ts.snap b/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/ui5-mock-yaml.test.ts.snap index 58b5837459..225812d78b 100644 --- a/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/ui5-mock-yaml.test.ts.snap +++ b/packages/mockserver-config-writer/test/unit/mockserver-config/__snapshots__/ui5-mock-yaml.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Test enhanceYaml() Create new ui5-mock.yaml based on ui5.yaml 1`] = ` +exports[`Test enhanceYaml() Create new ui5-mock.yaml based on ui5.yaml and manifest without dataSources 1`] = ` "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json specVersion: \\"2.5\\" @@ -21,16 +21,12 @@ server: beforeMiddleware: csp configuration: mountPath: / - services: - - urlPath: new/path/to/service - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data - generateMockData: true + services: [] annotations: [] " `; -exports[`Test enhanceYaml() Create new ui5-mock.yaml based on ui5.yaml, updated with annotations 1`] = ` +exports[`Test enhanceYaml() Create new ui5-mock.yaml based on ui5.yaml, updated with services and annotations 1`] = ` "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json specVersion: \\"2.5\\" @@ -52,9 +48,9 @@ server: configuration: mountPath: / services: - - urlPath: new/path/to/service - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/localService/SEPMRA_PROD_MAN.xml @@ -64,7 +60,7 @@ server: " `; -exports[`Test enhanceYaml() Create new ui5-mock.yaml with annotations from mock manifest.json 1`] = ` +exports[`Test enhanceYaml() Create new ui5-mock.yaml with services and annotations from mock manifest.json 1`] = ` "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json specVersion: \\"2.5\\" @@ -93,9 +89,9 @@ server: configuration: mountPath: / services: - - urlPath: /path/for/new/config - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/localService/SEPMRA_PROD_MAN.xml @@ -105,7 +101,7 @@ server: " `; -exports[`Test enhanceYaml() Create new ui5-mock.yaml without app name in manifest.json 1`] = ` +exports[`Test enhanceYaml() Create new ui5-mock.yaml without app name and dataSources in manifest.json 1`] = ` "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json specVersion: \\"2.5\\" @@ -133,16 +129,12 @@ server: beforeMiddleware: csp configuration: mountPath: / - services: - - urlPath: /path/for/new/config - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data - generateMockData: true + services: [] annotations: [] " `; -exports[`Test enhanceYaml() Update old ui5-mock.yaml with given path 1`] = ` +exports[`Test enhanceYaml() Update old ui5-mock.yaml with service overwrite 1`] = ` "specVersion: '2.0' metadata: name: 'app' @@ -155,16 +147,20 @@ server: configuration: mountPath: / services: - - urlPath: path/to/service - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/SEPMRA_PROD_MAN.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotation.xml + urlPath: annotations/annotation.xml - name: middleware-after " `; -exports[`Test enhanceYaml() Update ui5-mock.yaml, path from manifest 1`] = ` +exports[`Test enhanceYaml() Update ui5-mock.yaml, path and service name from manifest 1`] = ` "specVersion: '2.0' metadata: name: 'app' @@ -173,20 +169,23 @@ server: customMiddleware: - name: middleware-before - name: sap-fe-mockserver - beforeMiddleware: csp + beforeMiddleware: fiori-tools-proxy configuration: - mountPath: / services: + - urlPath: /some/previous/service/uri + metadataXmlPath: ./webapp/localService/previous-service/metadata.xml + mockdataRootPath: ./webapp/localService/previous-service/data + generateMockData: true - urlPath: ds/uri - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/ds/metadata.xml + mockdataPath: ./webapp/localService/ds/data generateMockData: true annotations: [] - name: middleware-after " `; -exports[`Test enhanceYaml() Update ui5-mock.yaml, path from manifest with annotations 1`] = ` +exports[`Test enhanceYaml() Update ui5-mock.yaml, path and service name from manifest with annotations 1`] = ` "specVersion: '2.0' metadata: name: 'app' @@ -195,13 +194,16 @@ server: customMiddleware: - name: middleware-before - name: sap-fe-mockserver - beforeMiddleware: csp + beforeMiddleware: fiori-tools-proxy configuration: - mountPath: / services: - - urlPath: '' - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /some/previous/service/uri + metadataXmlPath: ./webapp/localService/previous-service/metadata.xml + mockdataRootPath: ./webapp/localService/previous-service/data + generateMockData: true + - urlPath: /sap/opu/odata/sap/SEPMRA_PROD_MAN + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - localPath: ./webapp/localService/SEPMRA_PROD_MAN.xml diff --git a/packages/mockserver-config-writer/test/unit/mockserver-config/index.test.ts b/packages/mockserver-config-writer/test/unit/mockserver-config/index.test.ts index 17a3a92184..937ac50e99 100644 --- a/packages/mockserver-config-writer/test/unit/mockserver-config/index.test.ts +++ b/packages/mockserver-config-writer/test/unit/mockserver-config/index.test.ts @@ -3,7 +3,8 @@ import { join } from 'path'; import { generateMockserverConfig, removeMockserverConfig } from '../../../src'; describe('Test generateMockserverConfig()', () => { - test('Add config to bare minimum project', async () => { + test('Add config without any services to bare minimum project', async () => { + // Project hasn't any dataSources defined in manifest.json const basePath = join(__dirname, '../../fixtures/bare-minimum'); const webappPath = join(basePath, 'webapp'); @@ -17,6 +18,24 @@ describe('Test generateMockserverConfig()', () => { }); expect(fs.read(join(basePath, 'ui5-mock.yaml'))).toMatchSnapshot(); }); + + test('Add config with services to project', async () => { + // Project has dataSources defined in manifest.json and existing ones in ui5-mock.yaml, ones from manifest.json would be appended + const basePath = join(__dirname, '../../fixtures/ui5-mock-config'); + const webappPath = join(basePath, 'webapp'); + + const fs = await generateMockserverConfig(basePath, { + webappPath + }); + + expect(fs.readJSON(join(basePath, 'package.json'))).toEqual({ + 'name': 'ui5-mock-config', + 'devDependencies': { '@sap-ux/ui5-middleware-fe-mockserver': '2' }, + 'ui5': { 'dependencies': ['@sap-ux/ui5-middleware-fe-mockserver'] }, + 'scripts': { 'start-mock': 'fiori run --config ./ui5-mock.yaml --open "/"' } + }); + expect(fs.read(join(basePath, 'ui5-mock.yaml'))).toMatchSnapshot(); + }); }); describe('Test removeMockserverConfig()', () => { @@ -27,7 +46,7 @@ describe('Test removeMockserverConfig()', () => { const ui5MockYaml = join(basePath, 'ui5-mock.yaml'); expect(fs.exists(ui5MockYaml)).toBe(true); - removeMockserverConfig(basePath, fs); + await removeMockserverConfig(basePath, fs); expect(fs.exists(ui5MockYaml)).toBe(false); expect(fs.readJSON(join(basePath, 'package.json'))).toEqual({ 'name': 'bare-minimum' }); @@ -38,11 +57,27 @@ describe('Test removeMockserverConfig()', () => { const packageJsonPath = join(basePath, 'package.json'); const manifestPath = join(basePath, 'webapp/manifest.json'); - const fs = removeMockserverConfig(basePath); + const fs = await removeMockserverConfig(basePath); const packageJson = JSON.parse(await promises.readFile(packageJsonPath, { encoding: 'utf-8' })); const manifestJson = JSON.parse(await promises.readFile(manifestPath, { encoding: 'utf-8' })); expect(fs.readJSON(packageJsonPath)).toEqual(packageJson); expect(fs.readJSON(manifestPath)).toEqual(manifestJson); }); + + test('Remove from app with existing mockserver config from fs', async () => { + // Enhance manifest.json + const basePath = join(__dirname, '../../fixtures/ui5-mock-config'); + const ui5MockYaml = join(basePath, 'ui5-mock.yaml'); + const mockdataPaths = [ + join(basePath, 'webapp', 'localService', 'mainService', 'data'), + join(basePath, 'webapp', 'localService', 'STTA_SALES_ORDER_ND_SRV_01', 'data') + ]; + const fs = await removeMockserverConfig(basePath); + + expect(fs.exists(ui5MockYaml)).toBe(false); + mockdataPaths.forEach((mockdataPath) => { + expect(fs.exists(mockdataPath)).toBeFalsy(); + }); + }); }); diff --git a/packages/mockserver-config-writer/test/unit/mockserver-config/ui5-mock-yaml.test.ts b/packages/mockserver-config-writer/test/unit/mockserver-config/ui5-mock-yaml.test.ts index 423763a940..6e867d8d9f 100644 --- a/packages/mockserver-config-writer/test/unit/mockserver-config/ui5-mock-yaml.test.ts +++ b/packages/mockserver-config-writer/test/unit/mockserver-config/ui5-mock-yaml.test.ts @@ -11,13 +11,14 @@ describe('Test enhanceYaml()', () => { const ui5MockYamlPath = join(basePath, 'ui5-mock.yaml'); const webappPath = join('/webapp'); const manifestJsonPath = join(webappPath, 'manifest.json'); - const manifestWithMainService = `{"sap.ui5": { "models": { "": { "dataSource": "ds" } } },"sap.app": { "dataSources": { "ds": { "uri": "ds/uri/" } } }}`; + const manifestWithMainService = `{"sap.ui5": { "models": { "": { "dataSource": "ds" } } },"sap.app": { "dataSources": { "ds": { "uri": "ds/uri/", "type": "OData" } } }}`; const mockManifestJson = `{ "sap.app": { "id": "mockserverv2", "dataSources": { "mainService": { - "uri": "/sap/opu/odata/sap/SEPMRA_PROD_MAN/" + "uri": "/sap/opu/odata/sap/SEPMRA_PROD_MAN/", + "type": "OData" }, "SEPMRA_PROD_MAN": { "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/", @@ -34,21 +35,33 @@ describe('Test enhanceYaml()', () => { } } } + }, + "sap.ui5": { + "models": { + "": { + "dataSource": "mainService" + } + } } }`; beforeEach(() => { jest.clearAllMocks(); }); - test('Create new ui5-mock.yaml with annotations from mock manifest.json', async () => { + test('Create new ui5-mock.yaml with services and annotations from mock manifest.json', async () => { const fs = getFs({ [manifestJsonPath]: mockManifestJson }); - await enhanceYaml(fs, basePath, webappPath, { path: '/path/for/new/config' }); + await enhanceYaml(fs, basePath, webappPath); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); const ui5Config = await UI5Config.newInstance(fs.read(ui5MockYamlPath)); const mockserverConfig = ui5Config.findCustomMiddleware('sap-fe-mockserver'); - expect(mockserverConfig?.configuration.services?.[0].urlPath).toBe('/path/for/new/config'); + expect(mockserverConfig?.configuration.services?.[0]).toStrictEqual({ + generateMockData: true, + metadataPath: './webapp/localService/mainService/metadata.xml', + mockdataPath: './webapp/localService/mainService/data', + urlPath: '/sap/opu/odata/sap/SEPMRA_PROD_MAN' + }); expect(mockserverConfig?.configuration.annotations).toEqual([ { localPath: './webapp/localService/SEPMRA_PROD_MAN.xml', @@ -59,33 +72,38 @@ describe('Test enhanceYaml()', () => { ]); }); - test('Update ui5-mock.yaml, path from manifest', async () => { + test('Update ui5-mock.yaml, path and service name from manifest', async () => { const fs = getFsWithUi5MockYaml(manifestWithMainService); await enhanceYaml(fs, basePath, webappPath); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); }); - test('Update ui5-mock.yaml, path from manifest with annotations', async () => { + test('Update ui5-mock.yaml, path and service name from manifest with annotations', async () => { const fs = getFsWithUi5MockYaml(mockManifestJson); await enhanceYaml(fs, basePath, webappPath); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); }); - test('Update old ui5-mock.yaml with given path', async () => { - const fs = getFsWithUi5MockYaml('{}'); - await enhanceYaml(fs, basePath, webappPath, { path: 'path/to/service' }); + test('Update old ui5-mock.yaml with service overwrite', async () => { + const fs = getFsWithUi5MockYaml(mockManifestJson); + await enhanceYaml(fs, basePath, webappPath, { overwrite: true }); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); }); - test('Create new ui5-mock.yaml based on ui5.yaml, updated with annotations', async () => { + test('Create new ui5-mock.yaml based on ui5.yaml, updated with services and annotations', async () => { const fs = getFsWithUi5Yaml(mockManifestJson); - await enhanceYaml(fs, basePath, webappPath, { path: 'new/path/to/service' }); + await enhanceYaml(fs, basePath, webappPath); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); // additional check of urlPath, even if snapshot test get lightheartedly updated, the urlPath should remain stable. const ui5Config = await UI5Config.newInstance(fs.read(ui5MockYamlPath)); expect( - ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.services?.[0].urlPath - ).toBe('new/path/to/service'); + ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.services?.[0] + ).toStrictEqual({ + generateMockData: true, + metadataPath: './webapp/localService/mainService/metadata.xml', + mockdataPath: './webapp/localService/mainService/data', + urlPath: '/sap/opu/odata/sap/SEPMRA_PROD_MAN' + }); expect( ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.annotations ).toEqual([ @@ -98,26 +116,28 @@ describe('Test enhanceYaml()', () => { ]); }); - test('Create new ui5-mock.yaml based on ui5.yaml', async () => { + test('Create new ui5-mock.yaml based on ui5.yaml and manifest without dataSources', async () => { const fs = getFsWithUi5Yaml('{}'); - await enhanceYaml(fs, basePath, webappPath, { path: 'new/path/to/service' }); + await enhanceYaml(fs, basePath, webappPath); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); // additional check of urlPath, even if snapshot test get lightheartedly updated, the urlPath should remain stable. const ui5Config = await UI5Config.newInstance(fs.read(ui5MockYamlPath)); + // manifest without dataSources are used, so no services added expect( - ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.services?.[0].urlPath - ).toBe('new/path/to/service'); + ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.services + ).toStrictEqual([]); }); - test('Create new ui5-mock.yaml without app name in manifest.json', async () => { + test('Create new ui5-mock.yaml without app name and dataSources in manifest.json', async () => { const fs = getFs({ [manifestJsonPath]: '{}' }); - await enhanceYaml(fs, basePath, webappPath, { path: '/path/for/new/config' }); + await enhanceYaml(fs, basePath, webappPath); expect(fs.read(ui5MockYamlPath)).toMatchSnapshot(); // additional check of urlPath, even if snapshot test get lightheartedly updated, the urlPath should remain stable. const ui5Config = await UI5Config.newInstance(fs.read(ui5MockYamlPath)); + // manifest without dataSources are used, so no services added expect( - ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.services?.[0].urlPath - ).toBe('/path/for/new/config'); + ui5Config.findCustomMiddleware('sap-fe-mockserver')?.configuration.services + ).toStrictEqual([]); }); test(`Should throw error in case new added middleware can't be found by name 'sap-fe-mockserver'`, async () => { @@ -126,7 +146,7 @@ describe('Test enhanceYaml()', () => { findCustomMiddleware: () => undefined } as unknown as UI5Config); const fs = getFsWithUi5MockYaml('{}'); - await expect(enhanceYaml(fs, basePath, webappPath)).rejects.toThrow('mockserver'); + await expect(enhanceYaml(fs, basePath, webappPath, { overwrite: true })).rejects.toThrow('mockserver'); }); function getFs(files: { [path: string]: string }): Editor { @@ -149,12 +169,12 @@ server: - name: sap-fe-mockserver beforeMiddleware: fiori-tools-proxy configuration: - service: - urlBasePath: /some/previous/service/uri - name: '' - metadataXmlPath: ./webapp/localService/metadata.xml - mockdataRootPath: ./webapp/localService/data - generateMockData: true + services: + - urlPath: /some/previous/service/uri + metadataXmlPath: ./webapp/localService/previous-service/metadata.xml + mockdataRootPath: ./webapp/localService/previous-service/data + generateMockData: true + annotations: [] - name: middleware-after`, [manifestJsonPath]: manifestContent }); diff --git a/packages/odata-service-writer/src/data/annotations.ts b/packages/odata-service-writer/src/data/annotations.ts index 268113f1f7..ddb8328df0 100644 --- a/packages/odata-service-writer/src/data/annotations.ts +++ b/packages/odata-service-writer/src/data/annotations.ts @@ -3,27 +3,23 @@ import { t } from '../i18n'; import type { NamespaceAlias, OdataService, EdmxAnnotationsInfo } from '../types'; /** - * Returns the namespaces parsed from the specified metadata and annotations. + * Returns the namespaces parsed from the specified metadata and single annotation. * - * @param {Partial} service - an odata service where at least metadata and annotations properties are defined - * @param {string} service.metadata - OData service metadata xml - * @param {string} service.annotations - OData service annotations xml + * @param {EdmxAnnotationsInfo} edmxAnnotation - OData service annotations xml + * @param {NamespaceAlias[]} schemaNamespaces - namespaces array from metadata * @returns A reference to the namspaces array */ -export function getAnnotationNamespaces({ metadata, annotations }: Partial): NamespaceAlias[] { - // Enhance service with annotations namespaces - const schemaNamespaces = metadata ? getNamespaces(metadata) : []; - const edmxAnnotations = annotations as EdmxAnnotationsInfo; - if (edmxAnnotations?.xml) { +function getAnnotationNamespacesForSingleAnnotation( + edmxAnnotation: EdmxAnnotationsInfo, + schemaNamespaces: NamespaceAlias[] +): NamespaceAlias[] { + if (edmxAnnotation?.xml) { // Parse once - const annotationsJson: Object = xmlToJson(edmxAnnotations.xml); - + const annotationsJson: Object = xmlToJson(edmxAnnotation.xml); return schemaNamespaces.map((schema: NamespaceAlias) => { // Check if alias exists in backend annotation file, if so use it const annotationAlias = - edmxAnnotations.xml && schema.namespace - ? getAliasFromAnnotation(annotationsJson, schema.namespace) - : ''; + edmxAnnotation.xml && schema.namespace ? getAliasFromAnnotation(annotationsJson, schema.namespace) : ''; if (annotationAlias) { schema.alias = annotationAlias; } @@ -33,6 +29,29 @@ export function getAnnotationNamespaces({ metadata, annotations }: Partial} service - an odata service where at least metadata and annotations properties are defined + * @param {string} service.metadata - OData service metadata xml + * @param {string} service.annotations - OData service annotations xml + * @returns A reference to the namspaces array + */ +export function getAnnotationNamespaces({ metadata, annotations }: Partial): NamespaceAlias[] { + // Enhance service with annotations namespaces + let schemaNamespaces = metadata ? getNamespaces(metadata) : []; + if (Array.isArray(annotations)) { + for (const annotationName in annotations) { + const edmxAnnotation = annotations[annotationName] as EdmxAnnotationsInfo; + schemaNamespaces = getAnnotationNamespacesForSingleAnnotation(edmxAnnotation, schemaNamespaces); + } + } else { + const edmxAnnotation = annotations as EdmxAnnotationsInfo; + schemaNamespaces = getAnnotationNamespacesForSingleAnnotation(edmxAnnotation, schemaNamespaces); + } + return schemaNamespaces; +} + /** * Convert specified xml string to JSON. * diff --git a/packages/odata-service-writer/src/data/defaults.ts b/packages/odata-service-writer/src/data/defaults.ts index 8fdea531a2..23822ec36b 100644 --- a/packages/odata-service-writer/src/data/defaults.ts +++ b/packages/odata-service-writer/src/data/defaults.ts @@ -1,6 +1,10 @@ +import { join } from 'path'; import type { OdataService, EdmxAnnotationsInfo } from '../types'; import { ServiceType } from '../types'; import { DEFAULT_DATASOURCE_NAME } from './constants'; +import type { Manifest } from '@sap-ux/project-access'; +import { FileName, getWebappPath } from '@sap-ux/project-access'; +import type { Editor } from 'mem-fs-editor'; /** * Sets the default path for a given service. @@ -15,35 +19,86 @@ function setDefaultServicePath(service: OdataService): void { /** * Sets the default name for a given service. - * If the service name is not defined, it sets the name to `DEFAULT_DATASOURCE_NAME`. + * Default serivce name is used only for first service. * + * @param {string} basePath - the root path of an existing UI5 application * @param {OdataService} service - The service object whose name needs to be set or modified. + * @param fs - the memfs editor instance */ -function setDefaultServiceName(service: OdataService): void { - service.name = service.name ?? DEFAULT_DATASOURCE_NAME; +async function setDefaultServiceName(basePath: string, service: OdataService, fs: Editor): Promise { + const manifestPath = join(await getWebappPath(basePath, fs), FileName.Manifest); + const manifest = fs.readJSON(manifestPath) as unknown as Manifest; + // Check if manifest has already any dataSources defined, DEFAULT_DATASOURCE_NAME should be used for the first service + const dataSources = manifest?.['sap.app']?.dataSources; + if (dataSources) { + // Filter out ODataAnnotation dataSources and keep only OData ones + const oDataSources = Object.values(dataSources).filter((dataSource) => dataSource.type === 'OData'); + if (oDataSources.length === 0) { + service.name = DEFAULT_DATASOURCE_NAME; + } + } else { + // No existing dataSources - no existing services, use default name + service.name = DEFAULT_DATASOURCE_NAME; + } } /** * Sets the default model for a given service. - * If the service model is not defined, it sets the model to an empty string (Default UI5 model). + * Default UI5 model is used for first service model. + * For next services service model or service name is used as model (if model is not defined). * - * @param {OdataService} service - The service object whose model needs to be set or modified. + * @param {string} basePath - the root path of an existing UI5 application + * @param {OdataService} service - The service object whose model needs to be set or modified + * @param fs - the memfs editor instance */ -function setDefaultServiceModel(service: OdataService): void { - service.model = service.model ?? ''; // Default UI5 model +async function setDefaultServiceModel(basePath: string, service: OdataService, fs: Editor): Promise { + const manifestPath = join(await getWebappPath(basePath, fs), 'manifest.json'); + const manifest = fs.readJSON(manifestPath) as unknown as Manifest; + // Check if manifest has already any dataSource models defined, empty string '' should be used for the first service + const models = manifest?.['sap.ui5']?.models; + if (models) { + // Filter dataSource models by dataSource property + const servicesModels = Object.values(models).filter((model) => model.dataSource); + service.model = servicesModels.length === 0 ? '' : service.model ?? service.name; + } + // No models defined, that means first one is being added, set model to '' + service.model ??= ''; } /** - * Sets the default annotations name for a given service. - * If the service annotations name is not defined or empty, it creates a default annotations name + * Sets default annotation name for a single annotation of a given service. + * If the service annotation name is not defined or empty, it creates a default annotations name * from the technicalName by replacing all '/' characters with '_' and removing the leading '_'. + * If the service and annotation names are the same, then '_Annotation' string is added at the end of annotation name. + * + * @param {EdmxAnnotationsInfo} annotation - annotation of a given service + * @param {string} serviceName - name of the service whose annotations are getting modified. + */ +function setDefaultAnnotationName(annotation: EdmxAnnotationsInfo, serviceName?: string): void { + if (annotation?.technicalName && !annotation.name) { + annotation.name = annotation?.technicalName?.replace(/\//g, '_')?.replace(/^_/, ''); + } + if (annotation.name === serviceName) { + annotation.name += '_Annotation'; + } +} + +/** + * Sets default names for annotations of a given service. + * Handles single annotation in object or annotations array. * * @param {OdataService} service - The service object whose annotations name needs to be set or modified. */ function setDefaultAnnotationsName(service: OdataService): void { - const annotations = service.annotations as EdmxAnnotationsInfo; - if (annotations?.technicalName && !annotations.name) { - annotations.name = annotations?.technicalName?.replace(/\//g, '_')?.replace(/^_/, ''); + if (Array.isArray(service.annotations)) { + const annotations = service.annotations as EdmxAnnotationsInfo[]; + for (const annotationName in annotations) { + const annotation = annotations[annotationName]; + setDefaultAnnotationName(annotation, service.name); + } + } else if (service.annotations) { + const annotation = service.annotations as EdmxAnnotationsInfo; + setDefaultAnnotationName(annotation, service.name); } } @@ -51,12 +106,14 @@ function setDefaultAnnotationsName(service: OdataService): void { * Enhances the provided OData service object with path, name and model information. * Directly modifies the passed object reference. * - * @param {OdataService} service - the OData service object + * @param {string} basePath - the root path of an existing UI5 application + * @param {OdataService} service - the OData service instance + * @param {Editor} fs - the memfs editor instance */ -export function enhanceData(service: OdataService): void { +export async function enhanceData(basePath: string, service: OdataService, fs: Editor): Promise { setDefaultServicePath(service); - setDefaultServiceName(service); - setDefaultServiceModel(service); + await setDefaultServiceName(basePath, service, fs); + await setDefaultServiceModel(basePath, service, fs); // set service type to EDMX if not defined service.type = service.type ?? ServiceType.EDMX; /** @@ -69,10 +126,10 @@ export function enhanceData(service: OdataService): void { } // enhance preview settings with service configuration - service.previewSettings = service.previewSettings || {}; + service.previewSettings = service.previewSettings ?? {}; service.previewSettings.path = - service.previewSettings.path || `/${service.path?.split('/').filter((s: string) => s !== '')[0] ?? ''}`; - service.previewSettings.url = service.previewSettings.url || service.url || 'http://localhost'; + service.previewSettings.path ?? `/${service.path?.split('/').filter((s: string) => s !== '')[0] ?? ''}`; + service.previewSettings.url = service.previewSettings.url ?? service.url ?? 'http://localhost'; if (service.client && !service.previewSettings.client) { service.previewSettings.client = service.client; } diff --git a/packages/odata-service-writer/src/delete.ts b/packages/odata-service-writer/src/delete.ts new file mode 100644 index 0000000000..1678b4fdb6 --- /dev/null +++ b/packages/odata-service-writer/src/delete.ts @@ -0,0 +1,220 @@ +import { dirname, join, normalize, posix } from 'path'; +import { t } from './i18n'; +import type { Editor } from 'mem-fs-editor'; +import type { ManifestNamespace, Manifest } from '@sap-ux/project-access'; +import type { CdsAnnotationsInfo, EdmxAnnotationsInfo } from './types'; + +/** + * Removes the cds index or service file with the provided annotations. + * This function takes an Editor instance and cds annotations + * and deletes either from the index file or the service file with the given annotations. + * + * @param {Editor} fs - The memfs editor instance + * @param {CdsAnnotationsInfo} annotations - The cds annotations info. + * @returns {Promise} A promise that resolves when the cds files have been updated. + */ +async function removeCdsIndexOrServiceFile(fs: Editor, annotations: CdsAnnotationsInfo): Promise { + const dirPath = join(annotations.projectName, 'annotations'); + const annotationPath = normalize(dirPath).split(/[\\/]/g).join(posix.sep); + const annotationConfig = `\nusing from './${annotationPath}';`; + // Get index and service file paths + const indexFilePath = join(annotations.projectPath, annotations.appPath ?? '', 'index.cds'); + const serviceFilePath = join(annotations.projectPath, annotations.appPath ?? '', 'services.cds'); + // Remove annotation config from index or service file + if (indexFilePath && fs.exists(indexFilePath)) { + // Read old annotations content and replace it with empty string + const initialIndexContent = fs.read(indexFilePath); + const updatedContent = initialIndexContent.replace(annotationConfig, ''); + fs.write(indexFilePath, updatedContent); + } else if (fs.exists(serviceFilePath)) { + // Read old annotations content and replace it with empty string + const initialServiceFileContent = fs.read(serviceFilePath); + const updatedContent = initialServiceFileContent.replace(annotationConfig, ''); + fs.write(serviceFilePath, updatedContent); + } +} + +/** + * Removes annotations from CDS files. + * This function takes cds annotations and an Editor instance, + * then updates the relevant cds files with the given annotations. + * + * @param {CdsAnnotationsInfo} annotations - The cds annotations info. + * @param {Editor} fs - The memfs editor instance + * @returns {Promise} A promise that resolves when the cds files have been updated. + */ +export async function removeAnnotationsFromCDSFiles( + annotations: CdsAnnotationsInfo | CdsAnnotationsInfo[], + fs: Editor +): Promise { + if (Array.isArray(annotations)) { + for (const annotationName in annotations) { + const annotation = annotations[annotationName]; + const annotationCdsPath = join( + annotation.projectPath, + annotation.appPath ?? '', + annotation.projectName, + 'annotations.cds' + ); + // Remove from annotations.cds file + if (fs.exists(annotationCdsPath)) { + // Read old annotations content and replace it with empty string + const initialCDSContent = fs.read(annotationCdsPath); + const updatedContent = initialCDSContent.replace(annotation.cdsFileContents, ''); + fs.write(annotationCdsPath, updatedContent); + } + await removeCdsIndexOrServiceFile(fs, annotation); + } + } else { + const annotationCdsPath = join( + annotations.projectPath, + annotations.appPath ?? '', + annotations.projectName, + 'annotations.cds' + ); + // Write into annotations.cds file + if (fs.exists(annotationCdsPath)) { + // Read old annotations content and replace it with empty string + const initialCDSContent = fs.read(annotationCdsPath); + const updatedContent = initialCDSContent.replace(annotations.cdsFileContents, ''); + fs.write(annotationCdsPath, updatedContent); + } + await removeCdsIndexOrServiceFile(fs, annotations); + } +} + +/** + * Removes annotation XML files for EDMX annotations. + * + * @param {Editor} fs - The memfs editor instance. + * @param {string} basePath - The base path of the project. + * @param {string} serviceName - Name of The OData service. + * @param {OdataService} edmxAnnotations - The OData service annotations. + */ +export function removeAnnotationXmlFiles( + fs: Editor, + basePath: string, + serviceName: string, + edmxAnnotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[] +): void { + // Write annotation xml if annotations are provided and service type is EDMX + if (Array.isArray(edmxAnnotations)) { + for (const annotationName in edmxAnnotations) { + const annotation = edmxAnnotations[annotationName]; + const pathToAnnotationFile = join( + basePath, + 'webapp', + 'localService', + serviceName, + `${annotation.technicalName}.xml` + ); + if (fs.exists(pathToAnnotationFile)) { + fs.delete(pathToAnnotationFile); + } + } + } else if (edmxAnnotations?.xml) { + const pathToAnnotationFile = join( + basePath, + 'webapp', + 'localService', + serviceName, + `${edmxAnnotations.technicalName}.xml` + ); + if (fs.exists(pathToAnnotationFile)) { + fs.delete(pathToAnnotationFile); + } + } +} + +/** + * Internal function that removes files related to dataSource. + * + * @param fs - the memfs editor instance + * @param manifestPath - the root path of an existing UI5 application + * @param dataSource - name of the OData service instance + */ +function removeFileForDataSource(fs: Editor, manifestPath: string, dataSource: ManifestNamespace.DataSource): void { + const serviceSettings = dataSource.settings || {}; + if (serviceSettings?.localUri) { + const localUriPath = join(dirname(manifestPath), serviceSettings?.localUri); + if (fs.exists(localUriPath)) { + // delete the local data source file + fs.delete(localUriPath); + } + } +} + +/** + * Internal function that removes annotation files related to service. + * + * @param fs - the memfs editor instance + * @param manifestPath - the root path of an existing UI5 application + * @param annotations - annotations list + * @param dataSources - list of dataSources from manifest.json + */ +function removeAnnotations( + fs: Editor, + manifestPath: string, + annotations: string[], + dataSources?: { [k: string]: ManifestNamespace.DataSource } +): void { + for (const datasourceKey of annotations) { + const annotationDatasource = dataSources?.[datasourceKey]; + if (annotationDatasource?.type === 'ODataAnnotation') { + if (annotationDatasource.uri === annotationDatasource?.settings?.localUri) { + // This is localAnnotaton file. Do not delete it. + } else if (annotationDatasource) { + removeFileForDataSource(fs, manifestPath, annotationDatasource); + // delete dataSource from manifest + delete dataSources?.[datasourceKey]; + } + } + } +} + +/** + * Internal function that deletes service from the manifest.json based on the given service name. + * + * @param basePath - the root path of an existing UI5 application + * @param serviceName - name of the OData service instance + * @param fs - the memfs editor instance + */ +export function deleteServiceFromManifest(basePath: string, serviceName: string, fs: Editor): void { + const manifestPath = join(basePath, 'webapp', 'manifest.json'); + // Get component app id + const manifest = fs.readJSON(manifestPath) as unknown as Manifest; + const appProp = 'sap.app'; + const appid = manifest?.[appProp]?.id; + // Throw if required property is not found manifest.json + if (!appid) { + throw new Error( + t('error.requiredProjectPropertyNotFound', { property: `'${appProp}'.id`, path: manifestPath }) + ); + } + const dataSources = manifest?.[appProp]?.dataSources; + if (dataSources?.[serviceName]) { + removeFileForDataSource(fs, manifestPath, dataSources?.[serviceName]); + } + const serviceSettings = dataSources?.[serviceName]?.settings; + + // Check for linked backend annotations and delete if found. + if (serviceSettings?.annotations && serviceSettings.annotations.length > 0) { + removeAnnotations(fs, manifestPath, serviceSettings.annotations, dataSources); + } + // delete dataSource from manifest + if (dataSources?.[serviceName]) { + delete dataSources[serviceName]; + } + const modelsProp = 'sap.ui5'; + // delete models for this service + const models = manifest?.[modelsProp]?.models; + if (models) { + for (const modelKey of Object.keys(models)) { + const modelObj = models[modelKey]; + if (modelObj?.dataSource === serviceName) { + delete models[modelKey]; + } + } + } + fs.writeJSON(manifestPath, manifest); +} diff --git a/packages/odata-service-writer/src/index.ts b/packages/odata-service-writer/src/index.ts index eb0f62dec9..e7cce8e67c 100644 --- a/packages/odata-service-writer/src/index.ts +++ b/packages/odata-service-writer/src/index.ts @@ -3,11 +3,12 @@ import { create as createStorage } from 'mem-fs'; import type { Editor } from 'mem-fs-editor'; import { create } from 'mem-fs-editor'; import { updateManifest, updatePackageJson, updateCdsFilesWithAnnotations, writeAnnotationXmlFiles } from './updates'; -import type { FioriToolsProxyConfigBackend as ProxyBackend } from '@sap-ux/ui5-config'; +import type { CustomMiddleware, FioriToolsProxyConfigBackend as ProxyBackend } from '@sap-ux/ui5-config'; import { UI5Config, yamlErrorCode, YAMLError } from '@sap-ux/ui5-config'; import prettifyXml from 'prettify-xml'; import { enhanceData, getAnnotationNamespaces } from './data'; import { t } from './i18n'; +import type { EdmxOdataService, ProjectPaths } from './types'; import { OdataService, OdataVersion, @@ -18,6 +19,7 @@ import { } from './types'; import { getWebappPath } from '@sap-ux/project-access'; import { generateMockserverConfig } from '@sap-ux/mockserver-config-writer'; +import { deleteServiceFromManifest, removeAnnotationsFromCDSFiles, removeAnnotationXmlFiles } from './delete'; /** * Ensures the existence of the given files in the provided base path. If a file in the provided list does not exit, an error would be thrown. @@ -26,7 +28,7 @@ import { generateMockserverConfig } from '@sap-ux/mockserver-config-writer'; * @param files - list of files that need to exist * @param fs - the memfs editor instance */ -function ensureExists(basePath: string, files: string[], fs: Editor) { +function ensureExists(basePath: string, files: string[], fs: Editor): void { files.forEach((path) => { if (!fs.exists(join(basePath, path))) { throw new Error(t('error.requiredProjectFileNotFound', { path })); @@ -39,16 +41,13 @@ function ensureExists(basePath: string, files: string[], fs: Editor) { * * @param {string} basePath - the root path of an existing UI5 application * @param {Editor} [fs] - the memfs editor instance - * @returns an object with the optional locations of the package.json and ui5.yaml + * @returns an object with the optional locations of the package.json and ui5.yaml, ui5-local.yaml, ui5-mock.yaml */ -export async function findProjectFiles( - basePath: string, - fs: Editor -): Promise<{ packageJson?: string; ui5Yaml?: string }> { - const files: { packageJson?: string; ui5Yaml?: string } = {}; +export async function findProjectFiles(basePath: string, fs: Editor): Promise { + const files: ProjectPaths = {}; const parts = basePath.split(sep); - while (parts.length > 0 && (!files.packageJson || !files.ui5Yaml)) { + while (parts.length > 0 && (!files.packageJson || !files.ui5Yaml || !files.ui5LocalYaml || !files.ui5MockYaml)) { const path = parts.join(sep); if (!files.packageJson && fs.exists(join(path, 'package.json'))) { files.packageJson = join(path, 'package.json'); @@ -56,6 +55,12 @@ export async function findProjectFiles( if (!files.ui5Yaml && fs.exists(join(path, 'ui5.yaml'))) { files.ui5Yaml = join(path, 'ui5.yaml'); } + if (!files.ui5LocalYaml && fs.exists(join(path, 'ui5-local.yaml'))) { + files.ui5LocalYaml = join(path, 'ui5-local.yaml'); + } + if (!files.ui5MockYaml && fs.exists(join(path, 'ui5-mock.yaml'))) { + files.ui5MockYaml = join(path, 'ui5-mock.yaml'); + } parts.pop(); } @@ -63,117 +68,285 @@ export async function findProjectFiles( } /** - * Writes the odata service related file updates to an existing UI5 project specified by the base path. + * Generates mockserver middleware config for ui5-local.yaml file based on ui5-mock.yaml. * - * @param {string} basePath - the root path of an existing UI5 application - * @param {OdataService} service - the OData service instance - * @param {Editor} [fs] - the memfs editor instance - * @throws {Error} - if required UI5 project files are not found + * @param {Editor} fs - the memfs editor instance + * @param {OdataService} ui5YamlPath - path pointing to the ui5.yaml file + * @param {UI5Config} ui5LocalConfigPath - ui5-local.yaml configuration + * @param {string} ui5LocalConfig - path pointing to the ui5-local.yaml file * @returns {Promise} the updated memfs editor instance */ -async function generate(basePath: string, service: OdataService, fs?: Editor): Promise { - if (!fs) { - fs = create(createStorage()); +async function generateMockserverMiddlewareBasedOnUi5MockYaml( + fs: Editor, + ui5YamlPath: string, + ui5LocalConfigPath?: string, + ui5LocalConfig?: UI5Config +): Promise { + // Update ui5-local.yaml with mockserver middleware from ui5-mock.yaml + const ui5MockYamlPath = join(dirname(ui5YamlPath), 'ui5-mock.yaml'); + const ui5MockYamlConfig = await UI5Config.newInstance(fs.read(ui5MockYamlPath)); + const mockserverMiddlewareFromUi5Mock = ui5MockYamlConfig.findCustomMiddleware( + 'sap-fe-mockserver' + ) as CustomMiddleware; + if (ui5LocalConfigPath && fs.exists(ui5LocalConfigPath) && ui5LocalConfig && mockserverMiddlewareFromUi5Mock) { + ui5LocalConfig.updateCustomMiddleware(mockserverMiddlewareFromUi5Mock); } - const paths = await findProjectFiles(basePath, fs); - ensureExists(basePath, ['webapp/manifest.json'], fs); - enhanceData(service); - // set isServiceTypeEdmx true if service is EDMX - const isServiceTypeEdmx = service.type === ServiceType.EDMX; - // merge content into existing files - const templateRoot = join(__dirname, '../templates'); +} - // update cds files with annotations only if service type is CDS and annotations are provided - if (!isServiceTypeEdmx && service.annotations) { - await updateCdsFilesWithAnnotations(service.annotations as CdsAnnotationsInfo, fs); +/** + * Extends backend middleware for UI5Config with service data. + * + * @param {Editor} fs - the memfs editor instance + * @param {OdataService} service - the OData service instance data + * @param {UI5Config} ui5Config - UI5 configuration + * @param {string} ui5ConfigPath - path to the YAML config file + * @throws {Error} - if required UI5 project files are not found + */ +function extendBackendMiddleware(fs: Editor, service: OdataService, ui5Config: UI5Config, ui5ConfigPath: string): void { + try { + ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings as ProxyBackend); + } catch (error: any) { + if ( + (error instanceof YAMLError && error.code === yamlErrorCode.nodeNotFound) || + error.message === 'Could not find fiori-tools-proxy' + ) { + ui5Config.addFioriToolsProxydMiddleware({ + backend: [service.previewSettings as ProxyBackend], + ignoreCertError: service.ignoreCertError + }); + } else { + throw error; + } } - // manifest.json - updateManifest(basePath, service, fs, templateRoot); + fs.write(ui5ConfigPath, ui5Config.toString()); +} + +/** + * Returns all paths of the EDMX service annotations. + * + * @param {OdataService} edmxAnnotations - EDMX OData service annotations. + * @returns {string} annotation paths. + */ +function getEDMXAnnotationPaths(edmxAnnotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[]): string[] { + const emdxAnnotationsPaths: string[] = []; + if (Array.isArray(edmxAnnotations)) { + edmxAnnotations.forEach((annotation: EdmxAnnotationsInfo) => { + const technicalName = encodeURIComponent(annotation.technicalName); + emdxAnnotationsPaths.push( + `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='${technicalName}',Version='0001')/$value/` // This is how annotation paths are stored in manifest for ODataAnnotations + ); + }); + } else { + const technicalName = encodeURIComponent(edmxAnnotations.technicalName); + emdxAnnotationsPaths.push( + `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='${technicalName}',Version='0001')/$value/` + ); + } + return emdxAnnotationsPaths; +} - // update ui5.yaml if it exists +/** + * Writes local copies of metadata and annotations. + * + * @param {Editor} fs - the memfs editor instance + * @param {string} basePath - the root path of an existing UI5 application + * @param {string} webappPath - the webapp path of an existing UI5 application + * @param {string} templateRoot - path to the file templates + * @param {OdataService} service - the OData service instance with EDMX type + */ +async function writeLocalServiceFiles( + fs: Editor, + basePath: string, + webappPath: string, + templateRoot: string, + service: EdmxOdataService +): Promise { + // mainService should be used in case there is no name defined for service + fs.write( + join(webappPath, 'localService', service.name ?? 'mainService', 'metadata.xml'), + prettifyXml(service.metadata, { indent: 4 }) + ); + // Adds local annotations to datasources section of manifest.json and writes the annotations file + if (service.localAnnotationsName) { + const namespaces = getAnnotationNamespaces(service); + fs.copyTpl( + join(templateRoot, 'add', 'annotation.xml'), + join(basePath, 'webapp', 'annotations', `${service.localAnnotationsName}.xml`), + { ...service, namespaces } + ); + } +} + +/** + * Writes EDMX service data to ui5.yaml, ui5-mock.yaml, ui5-local.yaml, package.json and annotations xml files. + * + * @param {Editor} fs - the memfs editor instance + * @param {string} basePath - the root path of an existing UI5 application + * @param {ProjectPaths} paths - locations of the package.json and ui5.yaml, ui5-local.yaml, ui5-mock.yaml + * @param {string} templateRoot - path to the file templates + * @param {OdataService} service - the OData service instance with EDMX type + */ +async function writeEDMXServiceFiles( + fs: Editor, + basePath: string, + paths: ProjectPaths, + templateRoot: string, + service: EdmxOdataService +): Promise { let ui5Config: UI5Config | undefined; let ui5LocalConfig: UI5Config | undefined; - let ui5LocalConfigPath: string | undefined; - if (isServiceTypeEdmx && paths.ui5Yaml) { - // Dont extend backend middlewares if service type is CDS. + let ui5MockConfig: UI5Config | undefined; + if (paths.ui5Yaml) { ui5Config = await UI5Config.newInstance(fs.read(paths.ui5Yaml)); - try { - ui5Config.addBackendToFioriToolsProxydMiddleware(service.previewSettings as ProxyBackend); - } catch (error: any) { - if (error instanceof YAMLError && error.code === yamlErrorCode.nodeNotFound) { - ui5Config.addFioriToolsProxydMiddleware({ - backend: [service.previewSettings as ProxyBackend], - ignoreCertError: service.ignoreCertError - }); - } else { - throw error; - } - } - - fs.write(paths.ui5Yaml, ui5Config.toString()); - - // ui5-local.yaml - ui5LocalConfigPath = join(dirname(paths.ui5Yaml), 'ui5-local.yaml'); - if (fs.exists(ui5LocalConfigPath)) { - ui5LocalConfig = await UI5Config.newInstance(fs.read(ui5LocalConfigPath)); - ui5LocalConfig.addFioriToolsProxydMiddleware({ - backend: [service.previewSettings as ProxyBackend], - ignoreCertError: service.ignoreCertError - }); + // Update ui5.yaml with backend middleware + extendBackendMiddleware(fs, service, ui5Config, paths.ui5Yaml); + // Update ui5-local.yaml with backend middleware + if (paths.ui5LocalYaml) { + ui5LocalConfig = await UI5Config.newInstance(fs.read(paths.ui5LocalYaml)); + extendBackendMiddleware(fs, service, ui5LocalConfig, paths.ui5LocalYaml); } } - - // Add mockserver entries - if (isServiceTypeEdmx && service.metadata) { - // mockserver entries are not required if service type is CDS - // copy existing `ui5.yaml` as starting point for ui5-mock.yaml + if (service.metadata) { + const webappPath = await getWebappPath(basePath, fs); if (paths.ui5Yaml && ui5Config) { - const webappPath = await getWebappPath(basePath, fs); const config = { - webappPath: webappPath, - ui5MockYamlConfig: { path: service.path } + webappPath: webappPath }; + // Generate mockserver middleware for ui5-mock.yaml await generateMockserverConfig(basePath, config, fs); - // add mockserver middleware to ui5-local.yaml - if (ui5LocalConfig) { - ui5LocalConfig.addMockServerMiddleware(service.path); + // Update ui5-local.yaml with mockserver middleware from newly created/updated ui5-mock.yaml + await generateMockserverMiddlewareBasedOnUi5MockYaml(fs, paths.ui5Yaml, paths.ui5LocalYaml, ui5LocalConfig); + // Update ui5-mock.yaml with backend middleware + if (paths.ui5MockYaml) { + ui5MockConfig = await UI5Config.newInstance(fs.read(paths.ui5MockYaml)); + extendBackendMiddleware(fs, service, ui5MockConfig, paths.ui5MockYaml); } } - - // create local copy of metadata and annotations - fs.write( - join(basePath, 'webapp', 'localService', 'metadata.xml'), - prettifyXml(service.metadata, { indent: 4 }) - ); - - // Adds local annotations to datasources section of manifest.json and writes the annotations file - if (service.localAnnotationsName) { - const namespaces = getAnnotationNamespaces(service); - fs.copyTpl( - join(templateRoot, 'add', 'annotation.xml'), - join(basePath, 'webapp', 'annotations', `${service.localAnnotationsName}.xml`), - { ...service, namespaces } - ); - } + await writeLocalServiceFiles(fs, basePath, webappPath, templateRoot, service); } - - // update package.json for non-cap applications - if (isServiceTypeEdmx && paths.packageJson && paths.ui5Yaml) { + if (paths.packageJson && paths.ui5Yaml) { updatePackageJson(paths.packageJson, fs, !!service.metadata); } - - if (isServiceTypeEdmx && ui5LocalConfigPath && ui5LocalConfig) { + if (paths.ui5LocalYaml && ui5LocalConfig) { // write ui5 local yaml if service type is not CDS - fs.write(ui5LocalConfigPath, ui5LocalConfig.toString()); + fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString()); } + writeAnnotationXmlFiles(fs, basePath, service.name ?? 'mainService', service.annotations); +} - // Write annotation xml if annotations are provided and service type is EDMX +/** + * Writes the odata service related file updates to an existing UI5 project specified by the base path. + * + * @param {string} basePath - the root path of an existing UI5 application + * @param {OdataService} service - the OData service instance + * @param {Editor} [fs] - the memfs editor instance + * @throws {Error} - if required UI5 project files are not found + * @returns {Promise} the updated memfs editor instance + */ +async function generate(basePath: string, service: OdataService, fs?: Editor): Promise { + if (!fs) { + fs = create(createStorage()); + } + const paths = await findProjectFiles(basePath, fs); + ensureExists(basePath, ['webapp/manifest.json'], fs); + await enhanceData(basePath, service, fs); + // Set isServiceTypeEdmx true if service is EDMX + const isServiceTypeEdmx = service.type === ServiceType.EDMX; + // Prepare template folder for manifest and xml updates + const templateRoot = join(__dirname, '../templates'); + // Update manifest.json + updateManifest(basePath, service, fs, templateRoot); + // Dont extend backend and mockserver middlewares if service type is CDS if (isServiceTypeEdmx) { - writeAnnotationXmlFiles(fs, basePath, service); + await writeEDMXServiceFiles(fs, basePath, paths, templateRoot, service as EdmxOdataService); + } else if (!isServiceTypeEdmx && service.annotations) { + // Update cds files with annotations only if service type is CDS and annotations are provided + await updateCdsFilesWithAnnotations(service.annotations as CdsAnnotationsInfo | CdsAnnotationsInfo[], fs); } + return fs; +} +/** + * Removes service related data from project files for an existing UI5 project specified by the base path. + * Works as follow: + * 1. Service is removed from manifest. + * If service type is EDMX: + * 2. ui5.yaml + * - backend data of the service is removed from fiori-tools-proxy middleware + * 3. ui5-local.yaml + * - backend data of the service is removed from fiori-tools-proxy middleware + * - service is removed from mockserver middleware + * 4. ui5-mock.yaml + * - service is removed from mockserver middleware + * If service type is CDS: + * 2. annotations of the service are removed from CDS files. + * + * @param {string} basePath - the root path of an existing UI5 application + * @param {OdataService} service - the OData service instance + * @param {string} service.name - name of the OData service instance + * @param {string} service.path - path of the OData service instance + * @param {string} service.url - url of the OData service instance + * @param {ServiceType} service.type - type of the OData service instance + * @param {OdataService['annotations']} service.annotations - services annotations (EDMX or CDS) + * @param {Editor} [fs] - the memfs editor instance + * @throws {Error} - if required UI5 project files are not found + * @returns {Promise} the updated memfs editor instance + */ +async function remove( + basePath: string, + service: Required>, + fs?: Editor +): Promise { + if (!fs) { + fs = create(createStorage()); + } + let ui5Config: UI5Config | undefined; + let ui5LocalConfig: UI5Config | undefined; + let ui5MockConfig: UI5Config | undefined; + const paths = await findProjectFiles(basePath, fs); + // Delete service and it's annotations from manifest + deleteServiceFromManifest(basePath, service.name, fs); + const isServiceTypeEdmx = service.type === ServiceType.EDMX; + // Remove service related data from middlewares for EDMX services + if (isServiceTypeEdmx) { + // Delete service data from manifest.json + if (paths.ui5Yaml) { + ui5Config = await UI5Config.newInstance(fs.read(paths.ui5Yaml)); + // Delete service backend from fiori-tools-proxy middleware config + ui5Config.removeBackendFromFioriToolsProxydMiddleware(service.url); + fs.write(paths.ui5Yaml, ui5Config.toString()); + } + const serviceAnnotationPaths = getEDMXAnnotationPaths( + service.annotations as EdmxAnnotationsInfo | EdmxAnnotationsInfo[] + ); + if (paths.ui5LocalYaml) { + ui5LocalConfig = await UI5Config.newInstance(fs.read(paths.ui5LocalYaml)); + // Delete service backend from fiori-tools-proxy middleware config + ui5LocalConfig.removeBackendFromFioriToolsProxydMiddleware(service.url); + // Delete service from mockserver middleware config + ui5LocalConfig.removeServiceFromMockServerMiddleware(service.path, serviceAnnotationPaths); + fs.write(paths.ui5LocalYaml, ui5LocalConfig.toString()); + } + if (paths.ui5MockYaml) { + ui5MockConfig = await UI5Config.newInstance(fs.read(paths.ui5MockYaml)); + // Delete service backend from fiori-tools-proxy middleware config + ui5MockConfig.removeBackendFromFioriToolsProxydMiddleware(service.url); + // Delete service from mockserver config + ui5MockConfig.removeServiceFromMockServerMiddleware(service.path, serviceAnnotationPaths); + fs.write(paths.ui5MockYaml, ui5MockConfig.toString()); + } + removeAnnotationXmlFiles( + fs, + basePath, + service.name ?? 'mainService', + service.annotations as EdmxAnnotationsInfo | EdmxAnnotationsInfo[] + ); + } else { + // Remove annotations from CDS files based on annotations info + await removeAnnotationsFromCDSFiles(service.annotations as CdsAnnotationsInfo | CdsAnnotationsInfo[], fs); + } return fs; } -export { generate, OdataVersion, OdataService, ServiceType, EdmxAnnotationsInfo, CdsAnnotationsInfo }; +export { generate, remove, OdataVersion, OdataService, ServiceType, EdmxAnnotationsInfo, CdsAnnotationsInfo }; export { getAnnotationNamespaces, NamespaceAlias }; diff --git a/packages/odata-service-writer/src/types.ts b/packages/odata-service-writer/src/types.ts index dea7c5d77b..df9d8e8593 100644 --- a/packages/odata-service-writer/src/types.ts +++ b/packages/odata-service-writer/src/types.ts @@ -71,7 +71,7 @@ export interface OdataService { /** * Annotations can either be EDMX annotations or CDS annotations. */ - annotations?: EdmxAnnotationsInfo | CdsAnnotationsInfo; + annotations?: EdmxAnnotationsInfo | EdmxAnnotationsInfo[] | CdsAnnotationsInfo | CdsAnnotationsInfo[]; localAnnotationsName?: string; // The name used in the manifest.json and as the filename for local annotations previewSettings?: Partial; /** @@ -79,3 +79,14 @@ export interface OdataService { */ ignoreCertError?: boolean; } + +export type EdmxOdataService = Omit & { + annotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[]; +}; + +export interface ProjectPaths { + packageJson?: string; + ui5Yaml?: string; + ui5LocalYaml?: string; + ui5MockYaml?: string; +} diff --git a/packages/odata-service-writer/src/updates.ts b/packages/odata-service-writer/src/updates.ts index 9a5ceb6094..4291e3ae81 100644 --- a/packages/odata-service-writer/src/updates.ts +++ b/packages/odata-service-writer/src/updates.ts @@ -1,6 +1,6 @@ import { render } from 'ejs'; import type { Editor } from 'mem-fs-editor'; -import path, { join } from 'path'; +import { join, normalize, posix } from 'path'; import { t } from './i18n'; import type { OdataService, CdsAnnotationsInfo, EdmxAnnotationsInfo } from './types'; import semVer from 'semver'; @@ -15,7 +15,7 @@ import { getMinimumUI5Version, type Manifest, hasUI5CliV3 } from '@sap-ux/projec * @param fs - the memfs editor instance * @param templateRoot - root folder contain the ejs templates */ -export function updateManifest(basePath: string, service: OdataService, fs: Editor, templateRoot: string) { +export function updateManifest(basePath: string, service: OdataService, fs: Editor, templateRoot: string): void { const manifestPath = join(basePath, 'webapp', 'manifest.json'); // Get component app id const manifest = fs.readJSON(manifestPath) as unknown as Manifest; @@ -46,7 +46,7 @@ export function updateManifest(basePath: string, service: OdataService, fs: Edit */ async function updateCdsIndexOrServiceFile(fs: Editor, annotations: CdsAnnotationsInfo): Promise { const dirPath = join(annotations.projectName, 'annotations'); - const annotationPath = path.normalize(dirPath).split(/[\\/]/g).join(path.posix.sep); + const annotationPath = normalize(dirPath).split(/[\\/]/g).join(posix.sep); const annotationConfig = `\nusing from './${annotationPath}';`; // get index and service file paths const indexFilePath = join(annotations.projectPath, annotations.appPath ?? '', 'index.cds'); @@ -62,19 +62,34 @@ async function updateCdsIndexOrServiceFile(fs: Editor, annotations: CdsAnnotatio } /** - * Writes annotation XML files. + * Writes annotation XML files for EDMX service annotations. * * @param {Editor} fs - The memfs editor instance. * @param {string} basePath - The base path of the project. - * @param {OdataService} service - The OData service information. + * @param {string} serviceName - Name of The OData service. + * @param {OdataService} edmxAnnotations - The OData service annotations. */ -export function writeAnnotationXmlFiles(fs: Editor, basePath: string, service: OdataService): void { +export function writeAnnotationXmlFiles( + fs: Editor, + basePath: string, + serviceName: string, + edmxAnnotations: EdmxAnnotationsInfo | EdmxAnnotationsInfo[] +): void { // Write annotation xml if annotations are provided and service type is EDMX - const annotations = service.annotations as EdmxAnnotationsInfo; - if (annotations?.xml) { + if (Array.isArray(edmxAnnotations)) { + for (const annotationName in edmxAnnotations) { + const annotation = edmxAnnotations[annotationName]; + if (annotation?.xml) { + fs.write( + join(basePath, 'webapp', 'localService', serviceName, `${annotation.technicalName}.xml`), + prettifyXml(annotation.xml, { indent: 4 }) + ); + } + } + } else if (edmxAnnotations?.xml) { fs.write( - join(basePath, 'webapp', 'localService', `${annotations.technicalName}.xml`), - prettifyXml(annotations.xml, { indent: 4 }) + join(basePath, 'webapp', 'localService', serviceName, `${edmxAnnotations.technicalName}.xml`), + prettifyXml(edmxAnnotations.xml, { indent: 4 }) ); } } @@ -88,16 +103,38 @@ export function writeAnnotationXmlFiles(fs: Editor, basePath: string, service: O * @param {Editor} fs - The memfs editor instance * @returns {Promise} A promise that resolves when the cds files have been updated. */ -export async function updateCdsFilesWithAnnotations(annotations: CdsAnnotationsInfo, fs: Editor): Promise { - const annotationCdsPath = join( - annotations.projectPath, - annotations.appPath ?? '', - annotations.projectName, - 'annotations.cds' - ); - // write into annotations.cds file - fs.write(annotationCdsPath, annotations.cdsFileContents); - await updateCdsIndexOrServiceFile(fs, annotations); +export async function updateCdsFilesWithAnnotations( + annotations: CdsAnnotationsInfo | CdsAnnotationsInfo[], + fs: Editor +): Promise { + if (Array.isArray(annotations)) { + for (const annotationName in annotations) { + const annotation = annotations[annotationName]; + const annotationCdsPath = join( + annotation.projectPath, + annotation.appPath ?? '', + annotation.projectName, + 'annotations.cds' + ); + // write into annotations.cds file + if (fs.exists(annotationCdsPath)) { + fs.append(annotationCdsPath, annotation.cdsFileContents); + } else { + fs.write(annotationCdsPath, annotation.cdsFileContents); + } + await updateCdsIndexOrServiceFile(fs, annotation); + } + } else { + const annotationCdsPath = join( + annotations.projectPath, + annotations.appPath ?? '', + annotations.projectName, + 'annotations.cds' + ); + // write into annotations.cds file + fs.write(annotationCdsPath, annotations.cdsFileContents); + await updateCdsIndexOrServiceFile(fs, annotations); + } } /** @@ -106,7 +143,7 @@ export async function updateCdsFilesWithAnnotations(annotations: CdsAnnotationsI * @param minUI5Version - The minimum UI5 version. * @returns updated model settings. */ -function getModelSettings(minUI5Version: string | undefined) { +function getModelSettings(minUI5Version: string | undefined): { includeSynchronizationMode: boolean } { let includeSynchronizationMode = false; if (minUI5Version) { includeSynchronizationMode = semVer.satisfies(minUI5Version, '<=1.110'); @@ -120,7 +157,7 @@ function getModelSettings(minUI5Version: string | undefined) { * @param fs - the memfs editor instance * @param addMockServer true if the mocksever middleware needs to be added as well */ -export function updatePackageJson(path: string, fs: Editor, addMockServer: boolean) { +export function updatePackageJson(path: string, fs: Editor, addMockServer: boolean): void { const packageJson = JSON.parse(fs.read(path)); packageJson.devDependencies = packageJson.devDependencies ?? {}; if (!hasUI5CliV3(packageJson.devDependencies)) { diff --git a/packages/odata-service-writer/templates/extend/manifest.json b/packages/odata-service-writer/templates/extend/manifest.json index 2b4c005945..3f6665530d 100644 --- a/packages/odata-service-writer/templates/extend/manifest.json +++ b/packages/odata-service-writer/templates/extend/manifest.json @@ -5,22 +5,52 @@ "uri": "<%=path%>", "type": "OData", "settings": { - "annotations": [<%if (locals.annotations && annotations.technicalName) {%> - "<%- annotations.name %>"<% if (locals.localAnnotationsName) {%>,<%}%><% } %><% if (locals.localAnnotationsName) { %> - "<%- localAnnotationsName %>"<% } %> - ]<% if (locals.metadata) { %>, - "localUri": "localService/metadata.xml" <% } %><%if (version === '4') {%>, + "annotations": [ + <% if (locals.annotations && typeof annotations !== 'undefined') { %> + <% if (annotations.length) { %> + <% annotations.forEach(function(annotation, index) { %> + <% if (annotation.technicalName) { %> + "<%= annotation.name %>"<% if (index < annotations.length - 1) { %>,<% } %> + <% } %> + <% }); %> + <% } else if (annotations.technicalName) { %> + "<%= annotations.name %>" + <% } %> + <% if (locals.localAnnotationsName) {%>,<%}%><% } %> + <% if (locals.localAnnotationsName) { %> + "<%- localAnnotationsName %>" + <% } %> + ] + <% if (locals.metadata) { %>, + "localUri": "localService/<%- name %>/metadata.xml" <% } %><%if (version === '4') {%>, "odataVersion": "4.0"<% } %><%if (version === '2') {%>, "odataVersion": "2.0"<% } %> } - }<%if (locals.annotations && annotations.technicalName) {%>, - "<%- annotations.name %>": { - "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='<%- encodeURIComponent(annotations.technicalName) %>',Version='0001')/$value/", - "type": "ODataAnnotation", - "settings": { - "localUri": "localService/<%- annotations.technicalName %>.xml" - } - }<% } %><% if (locals.localAnnotationsName) { %>, + } + <% if (locals.annotations && typeof annotations !== 'undefined') { %> + <% if (annotations.length && annotations.length > 0) { %> + <% annotations.forEach(function(annotation, index) { %> + <% if (annotation.technicalName) { %>, + "<%= annotation.name %>": { + "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='<%- encodeURIComponent(annotation.technicalName) %>',Version='0001')/$value/", + "type": "ODataAnnotation", + "settings": { + "localUri": "localService/<%- name %>/<%- annotation.technicalName %>.xml" + } + } + <% } %> + <% }); %> + <% } else if (annotations.technicalName) { %>, + "<%= annotations.name %>": { + "uri": "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='<%- encodeURIComponent(annotations.technicalName) %>',Version='0001')/$value/", + "type": "ODataAnnotation", + "settings": { + "localUri": "localService/<%- name %>/<%- annotations.technicalName %>.xml" + } + } + <% } %> + <% } %> + <% if (locals.localAnnotationsName) { %>, "<%- localAnnotationsName %>": { "type": "ODataAnnotation", "uri": "annotations/<%- localAnnotationsName %>.xml", diff --git a/packages/odata-service-writer/test/__snapshots__/index.test.ts.snap b/packages/odata-service-writer/test/__snapshots__/index.test.ts.snap index d6a5e4c381..1c802aa44d 100644 --- a/packages/odata-service-writer/test/__snapshots__/index.test.ts.snap +++ b/packages/odata-service-writer/test/__snapshots__/index.test.ts.snap @@ -37,10 +37,14 @@ Object { mountPath: / services: - urlPath: /sap/odata/testme - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService/sepmra_annotations_tech_name.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='sepmra_annotations_tech_name',Version='0001')/$value/ + - localPath: ./webapp/annotations/annotations_test.xml + urlPath: annotations/annotations_test.xml ", "state": "modified", }, @@ -65,11 +69,11 @@ Object { mountPath: / services: - urlPath: /sap/odata/testme - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService/sepmra_annotations_tech_name.xml + - localPath: ./webapp/localService/mainService/sepmra_annotations_tech_name.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='sepmra_annotations_tech_name',Version='0001')/$value/ - localPath: ./webapp/annotations/annotations_test.xml urlPath: annotations/annotations_test.xml @@ -115,7 +119,7 @@ Object { ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -1926,7 +1930,7 @@ Object { ", "state": "modified", }, - "webapp/localService/sepmra_annotations_tech_name.xml": Object { + "webapp/localService/mainService/sepmra_annotations_tech_name.xml": Object { "contents": " @@ -2740,7 +2744,7 @@ Object { \\"sepmra_annotations_tech_name\\", \\"annotations_test\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"4.0\\" } }, @@ -2748,7 +2752,7 @@ Object { \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='sepmra_annotations_tech_name',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService/sepmra_annotations_tech_name.xml\\" + \\"localUri\\": \\"localService/mainService/sepmra_annotations_tech_name.xml\\" } }, \\"annotations_test\\": { @@ -2817,10 +2821,12 @@ Object { mountPath: / services: - urlPath: /sap/odata/testme - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true - annotations: [] + annotations: + - localPath: ./webapp/localService/mainService//SEPM_XYZ/SERVICE.xml + urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='%2FSEPM_XYZ%2FSERVICE',Version='0001')/$value/ ", "state": "modified", }, @@ -2845,11 +2851,11 @@ Object { mountPath: / services: - urlPath: /sap/odata/testme - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + metadataPath: ./webapp/localService/mainService/metadata.xml + mockdataPath: ./webapp/localService/mainService/data generateMockData: true annotations: - - localPath: ./webapp/localService//SEPM_XYZ/SERVICE.xml + - localPath: ./webapp/localService/mainService//SEPM_XYZ/SERVICE.xml urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='%2FSEPM_XYZ%2FSERVICE',Version='0001')/$value/ ", "state": "modified", @@ -2872,7 +2878,7 @@ Object { ", "state": "modified", }, - "webapp/localService/SEPM_XYZ/SERVICE.xml": Object { + "webapp/localService/mainService/SEPM_XYZ/SERVICE.xml": Object { "contents": " @@ -2880,7 +2886,7 @@ Object { ", "state": "modified", }, - "webapp/localService/metadata.xml": Object { + "webapp/localService/mainService/metadata.xml": Object { "contents": " @@ -2901,7 +2907,7 @@ Object { \\"annotations\\": [ \\"SEPM_XYZ_SERVICE\\" ], - \\"localUri\\": \\"localService/metadata.xml\\", + \\"localUri\\": \\"localService/mainService/metadata.xml\\", \\"odataVersion\\": \\"2.0\\" } }, @@ -2909,7 +2915,7 @@ Object { \\"uri\\": \\"/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='%2FSEPM_XYZ%2FSERVICE',Version='0001')/$value/\\", \\"type\\": \\"ODataAnnotation\\", \\"settings\\": { - \\"localUri\\": \\"localService//SEPM_XYZ/SERVICE.xml\\" + \\"localUri\\": \\"localService/mainService//SEPM_XYZ/SERVICE.xml\\" } } } diff --git a/packages/odata-service-writer/test/index.test.ts b/packages/odata-service-writer/test/index.test.ts index 680c576e70..3f18b45dc2 100644 --- a/packages/odata-service-writer/test/index.test.ts +++ b/packages/odata-service-writer/test/index.test.ts @@ -112,4 +112,23 @@ describe('ODataService templates', () => { const packagePath = join(testDir, 'package.json'); expect(fs.readJSON(packagePath)).toEqual({}); }); + + it('Verify generated project of type CDS updates CDS annotation files', async () => { + const config: OdataService = { + url: 'http://localhost', + path: '/sap/odata/testme', + version: OdataVersion.v2, + type: ServiceType.CDS, + annotations: { + cdsFileContents: '"using AdminService as service from \'../../srv/admin-service\';"', + projectPath: 'testProject', + appPath: 'webapp', + projectName: 'annotations' + } + }; + const testDir = await createTestDir('cds-service'); + await generate(testDir, config, fs); + const annotationCdsPath = join('testProject', 'webapp', 'annotations', 'annotations.cds'); + expect(fs.read(annotationCdsPath)).toBe('"using AdminService as service from \'../../srv/admin-service\';"'); + }); }); diff --git a/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-multiple-annotations.ts b/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-multiple-annotations.ts new file mode 100644 index 0000000000..df815c8de1 --- /dev/null +++ b/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-multiple-annotations.ts @@ -0,0 +1,45 @@ +export const expectedEdmxManifestMultipleAnnotations = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + aname: { + uri: '/a/path', + type: 'OData', + settings: { + annotations: ['annotation1', 'annotation2', 'localTest'], + odataVersion: '2.0' + } + }, + annotation1: { + uri: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='annotation1Technical',Version='0001')/$value/`, + type: 'ODataAnnotation', + settings: { + localUri: 'localService/aname/annotation1Technical.xml' + } + }, + annotation2: { + uri: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='annotation2Technical',Version='0001')/$value/`, + type: 'ODataAnnotation', + settings: { + localUri: 'localService/aname/annotation2Technical.xml' + } + }, + localTest: { + type: 'ODataAnnotation', + uri: 'annotations/localTest.xml', + settings: { + localUri: 'annotations/localTest.xml' + } + } + } + }, + 'sap.ui5': { + models: { + amodel: { + dataSource: 'aname', + preload: true, + settings: {} + } + } + } +}; diff --git a/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-multiple-services.ts b/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-multiple-services.ts new file mode 100644 index 0000000000..efec04d74f --- /dev/null +++ b/packages/odata-service-writer/test/test-data/manifest-json/edmx-manifest-multiple-services.ts @@ -0,0 +1,44 @@ +export const expectedEdmxManifestMultipleServices = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + mainService: { + type: 'OData' + }, + aname: { + uri: '/a/path', + type: 'OData', + settings: { + annotations: ['annotation1', 'localTest'], + odataVersion: '2.0' + } + }, + annotation1: { + uri: "/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='annotation1Technical',Version='0001')/$value/", + type: 'ODataAnnotation', + settings: { + localUri: 'localService/aname/annotation1Technical.xml' + } + }, + localTest: { + type: 'ODataAnnotation', + uri: 'annotations/localTest.xml', + settings: { + localUri: 'annotations/localTest.xml' + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService' + }, + amodel: { + dataSource: 'aname', + preload: true, + settings: {} + } + } + } +}; diff --git a/packages/odata-service-writer/test/unit/annotations.test.ts b/packages/odata-service-writer/test/unit/annotations.test.ts index 5ef2d736ce..e4e106f727 100644 --- a/packages/odata-service-writer/test/unit/annotations.test.ts +++ b/packages/odata-service-writer/test/unit/annotations.test.ts @@ -36,6 +36,14 @@ describe('metadata parsing', () => { }); it('getAnnotationNamespaces: annotations alias parsing', () => { + // Check annotations defined in array + expect( + getAnnotationNamespaces({ + metadata: metadata, + annotations: [{ technicalName: 'TEST_ANNOTATIONS', xml: annotationsMultipleRef }] + }) + ).toEqual([{ namespace: 'SEPMRA_PROD_MAN', alias: 'SAP' }]); + // Check annotations defined ar single object expect( getAnnotationNamespaces({ metadata: metadata, diff --git a/packages/odata-service-writer/test/unit/delete.test.ts b/packages/odata-service-writer/test/unit/delete.test.ts new file mode 100644 index 0000000000..c3393a4315 --- /dev/null +++ b/packages/odata-service-writer/test/unit/delete.test.ts @@ -0,0 +1,248 @@ +import { deleteServiceFromManifest, removeAnnotationsFromCDSFiles, removeAnnotationXmlFiles } from '../../src/delete'; +import type { Editor } from 'mem-fs-editor'; +import { create } from 'mem-fs-editor'; +import { create as createStorage } from 'mem-fs'; +import { dirname, join } from 'path'; +import type { CdsAnnotationsInfo } from '../../src'; +import { updateCdsFilesWithAnnotations } from '../../src/updates'; + +describe('removeAnnotationsFromCDSFiles', () => { + let fs: Editor; + beforeEach(async () => { + fs = create(createStorage()); + }); + it('removes from annotation cds files correctly', async () => { + const annotationsInfo: CdsAnnotationsInfo = { + cdsFileContents: '"using AdminService as service from \'../../srv/admin-service\';"', + projectPath: 'testProject', + appPath: 'webapp', + projectName: 'annotations' + }; + const annotationPath = join('./testProject/webapp/annotations', 'annotations.cds'); + + // Write annotation file + await updateCdsFilesWithAnnotations(annotationsInfo, fs); + let annotationCds = fs.read(annotationPath); + expect(annotationCds).toEqual(annotationsInfo.cdsFileContents); + + // Remove from annotation file + await removeAnnotationsFromCDSFiles(annotationsInfo, fs); + annotationCds = fs.read(annotationPath); + expect(annotationCds).toEqual(''); + + // Convert the annotation path to the services path + const serviceCdsPath = join(dirname(annotationPath).replace('annotations', ''), 'services.cds'); + const serviceCds = fs.read(serviceCdsPath); + expect(serviceCds).not.toContain(`using from './annotations/annotations';`); + }); + + it('removes from annotations cds files correctly for multiple annotations', async () => { + const annotationsInfo: CdsAnnotationsInfo[] = [ + { + cdsFileContents: '"using AdminService as service from \'../../srv/admin-service\';"', + projectPath: 'testProject', + appPath: 'webapp', + projectName: 'annotations' + }, + { + cdsFileContents: '"using IncidentService as service from \'../../srv/incidentservice\';"', + projectPath: 'testProject', + appPath: 'webapp', + projectName: 'annotations' + } + ]; + const annotationsPath = join('./testProject/webapp/annotations', 'annotations.cds'); + + // Write annotation file + await updateCdsFilesWithAnnotations(annotationsInfo, fs); + let annotationCds = fs.read(annotationsPath); + expect(annotationCds).toContain(annotationsInfo[0].cdsFileContents); + expect(annotationCds).toContain(annotationsInfo[1].cdsFileContents); + + // Remove from annotation file + await removeAnnotationsFromCDSFiles(annotationsInfo, fs); + annotationCds = fs.read(annotationsPath); + expect(annotationCds).not.toContain(annotationsInfo[0].cdsFileContents); + expect(annotationCds).not.toContain(annotationsInfo[1].cdsFileContents); + + // Convert the annotation path to the services path + const serviceCdsPath = join(dirname(annotationsPath).replace('annotations', ''), 'services.cds'); + const serviceCds = fs.read(serviceCdsPath); + expect(serviceCds).not.toContain(`using from './annotations/annotations';`); + }); +}); + +describe('deleteServiceFromManifest', () => { + let fs: Editor; + beforeEach(async () => { + fs = create(createStorage()); + }); + describe('deleteServiceFromManifest', () => { + test('Ensure all references for service are deleted in edmx projects', async () => { + const metadaPath = './webapp/localService/mainService/metadata.xml'; + const testManifest = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + mainService: { + uri: '/sap/opu/odata/sap/SEPMRA_PROD_MAN/', + type: 'OData', + settings: { + annotations: [], + localUri: 'localService/mainService/metadata.xml', + odataVersion: '2.0' + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService', + preload: true, + settings: {} + } + } + } + }; + + fs.writeJSON('./webapp/manifest.json', testManifest); + fs.writeJSON(metadaPath, ''); + // Call deleteServiceFromManifest + deleteServiceFromManifest('./', 'mainService', fs); + const manifestJson = fs.readJSON('./webapp/manifest.json') as any; + expect(manifestJson?.['sap.app']?.dataSources).toEqual({}); + expect(manifestJson?.['sap.ui5']?.models).toEqual({}); + // Metadata file for dataSource should be deleted as well + expect(fs.exists(metadaPath)).toBeFalsy(); + }); + + test('Ensure all references for service with multiple annotations are deleted in edmx projects', async () => { + const metadaPath = './webapp/localService/mainService/metadata.xml'; + const testManifest = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + mainService: { + uri: '/sap/opu/odata/sap/SEPMRA_PROD_MAN/', + type: 'OData', + settings: { + annotations: ['SEPMRA_PROD_MAN', 'annotation'], + localUri: 'localService/mainService/metadata.xml', + odataVersion: '2.0' + } + }, + SEPMRA_PROD_MAN: { + uri: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/`, + type: 'ODataAnnotation', + settings: { + localUri: 'localService/SEPMRA_PROD_MAN.xml' + } + }, + annotation: { + type: 'ODataAnnotation', + uri: 'annotations/annotation.xml', + settings: { + localUri: 'annotations/annotation.xml' + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService', + preload: true, + settings: {} + } + } + } + }; + + fs.writeJSON('./webapp/manifest.json', testManifest); + fs.writeJSON(metadaPath, ''); + // Call deleteServiceFromManifest + deleteServiceFromManifest('./', 'mainService', fs); + const manifestJson = fs.readJSON('./webapp/manifest.json') as any; + expect(manifestJson?.['sap.app']?.dataSources).toEqual({ + annotation: { + type: 'ODataAnnotation', + uri: 'annotations/annotation.xml', + settings: { + localUri: 'annotations/annotation.xml' + } + } + }); + expect(manifestJson?.['sap.ui5']?.models).toEqual({}); + // Metadata file for dataSource should be deleted as well + expect(fs.exists(metadaPath)).toBeFalsy(); + }); + + test('Ensure other services are not deleted in edmx projects', async () => { + const metadaPath = './webapp/localService/mainService/metadata.xml'; + const testManifest = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + mainService: { + uri: '/sap/opu/odata/sap/SEPMRA_PROD_MAN/', + type: 'OData', + settings: { + annotations: [], + localUri: 'localService/mainService/metadata.xml', + odataVersion: '2.0' + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService', + preload: true, + settings: {} + } + } + } + }; + + fs.writeJSON('./webapp/manifest.json', testManifest); + fs.writeJSON(metadaPath, ''); + // Call deleteServiceFromManifest + deleteServiceFromManifest('./', 'dummyService', fs); + const manifestJson = fs.readJSON('./webapp/manifest.json'); + expect(manifestJson).toEqual(testManifest); + // Metadata files for other services should not be deleted as well + expect(fs.exists(metadaPath)).toBeTruthy(); + }); + }); +}); + +describe('removeAnnotationXmlFiles', () => { + let fs: Editor; + beforeEach(async () => { + fs = create(createStorage()); + }); + + it('removes service annotations', async () => { + const serviceAnnotationPath = join('', 'webapp', 'localService', 'mainService', 'annotation1.xml'); + fs.write(serviceAnnotationPath, ''); + const differentServiceAnnotationPath = join( + '', + 'webapp', + 'localService', + 'differentService', + 'annotation1.xml' + ); + fs.write(differentServiceAnnotationPath, ''); + await removeAnnotationXmlFiles(fs, '', 'mainService', [ + { + name: 'annotation1', + technicalName: 'annotation1', + xml: '' + } + ]); + expect(fs.exists(serviceAnnotationPath)).toBe(false); + expect(fs.exists(differentServiceAnnotationPath)).toBe(true); + }); +}); diff --git a/packages/odata-service-writer/test/unit/index.test.ts b/packages/odata-service-writer/test/unit/index.test.ts index c67d7d9090..bc013176a8 100644 --- a/packages/odata-service-writer/test/unit/index.test.ts +++ b/packages/odata-service-writer/test/unit/index.test.ts @@ -1,5 +1,5 @@ -import type { OdataService } from '../../src'; -import { generate, OdataVersion, ServiceType } from '../../src'; +import type { EdmxAnnotationsInfo, OdataService } from '../../src'; +import { generate, remove, OdataVersion, ServiceType } from '../../src'; import { join } from 'path'; import type { Editor } from 'mem-fs-editor'; import { create } from 'mem-fs-editor'; @@ -160,10 +160,53 @@ describe('generate', () => { expect(manifest['sap.app'].dataSources.mainService.uri).toBe(config.path); expect(manifest['sap.app'].dataSources[config.annotations.technicalName]).toBeDefined(); // verify local copy of metadata - expect(fs.read(join(testDir, 'webapp', 'localService', 'metadata.xml'))).toBe(config.metadata); - expect(fs.read(join(testDir, 'webapp', 'localService', `${config.annotations.technicalName}.xml`))).toBe( - config.annotations.xml + expect(fs.read(join(testDir, 'webapp', 'localService', 'mainService', 'metadata.xml'))).toBe( + config.metadata ); + expect( + fs.read( + join(testDir, 'webapp', 'localService', 'mainService', `${config.annotations.technicalName}.xml`) + ) + ).toBe(config.annotations.xml); + }); + + it('Valid OData V2 service with multiple annotations', async () => { + const config = { + ...commonConfig, + version: OdataVersion.v2, + annotations: [ + { + technicalName: 'TEST_ME', + xml: '' + }, + { + technicalName: 'TEST_ME_TWO', + xml: '' + } + ] + }; + await generate(testDir, config as OdataService, fs); + + // verify updated manifest.json + const manifest = fs.readJSON(join(testDir, 'webapp', 'manifest.json')) as any; + expect(manifest['sap.app'].dataSources.mainService.uri).toBe(config.path); + expect(manifest['sap.app'].dataSources[config.annotations[0].technicalName]).toBeDefined(); + expect(manifest['sap.app'].dataSources[config.annotations[1].technicalName]).toBeDefined(); + // verify local copy of metadata + // mainService should be used in case there is no name defined for service + expect(fs.read(join(testDir, 'webapp', 'localService', 'mainService', 'metadata.xml'))).toBe( + config.metadata + ); + expect( + fs.read( + join(testDir, 'webapp', 'localService', 'mainService', `${config.annotations[0].technicalName}.xml`) + ) + ).toBe(config.annotations[0].xml); + expect( + fs.read( + join(testDir, 'webapp', 'localService', 'mainService', `${config.annotations[1].technicalName}.xml`) + ) + ).toBe(config.annotations[1].xml); }); it('Valid OData V4 service', async () => { @@ -178,7 +221,12 @@ describe('generate', () => { const manifest = fs.readJSON(join(testDir, 'webapp', 'manifest.json')) as any; expect(manifest['sap.app'].dataSources[config.name].uri).toBe(config.path); // verify local copy of metadata - expect(fs.read(join(testDir, 'webapp', 'localService', 'metadata.xml'))).toBe(config.metadata); + // first service is always mainService, so we make sure data for it is generated in correct location + expect(fs.exists(join(testDir, 'webapp', 'localService', 'myService', 'metadata.xml'))).toBe(false); + expect(fs.exists(join(testDir, 'webapp', 'localService', 'mainService', 'metadata.xml'))).toBe(true); + expect(fs.read(join(testDir, 'webapp', 'localService', 'mainService', 'metadata.xml'))).toBe( + config.metadata + ); // verify that no destination is added to the ui5.yaml expect(fs.read(join(testDir, 'ui5.yaml'))).not.toContain('destination: '); // verify that client is set @@ -276,9 +324,9 @@ describe('generate', () => { path: '/V2/Northwind/Northwind.svc', version: OdataVersion.v2 } as OdataService; - + // No services are defined - mainService used for service name and '' for service model let configCopy = cloneDeep(config); - enhanceData(configCopy); + await enhanceData('', configCopy, fs); expect(configCopy).toMatchInlineSnapshot(` Object { "model": "", @@ -294,8 +342,13 @@ describe('generate', () => { } `); + // Services already defined - actual service name and service model are used configCopy = cloneDeep(Object.assign({}, config, { model: 'modelName', name: 'datasourceName' })); - enhanceData(configCopy); + fs.writeJSON(join('webapp', 'manifest.json'), { + 'sap.app': { dataSources: { existingService: { type: 'OData' } } }, + 'sap.ui5': { models: { existingModel: { dataSource: 'existingService' } } } + }); + await enhanceData('', configCopy, fs); expect(configCopy).toMatchInlineSnapshot(` Object { "model": "modelName", @@ -311,9 +364,10 @@ describe('generate', () => { } `); + fs.delete(join('webapp', 'manifest.json')); // Undefined path does not throw but sets valid path configCopy = cloneDeep(Object.assign({}, config, { path: undefined })); - enhanceData(configCopy); + await enhanceData('', configCopy, fs); expect(configCopy).toMatchInlineSnapshot(` Object { "model": "", @@ -328,6 +382,199 @@ describe('generate', () => { "version": "2", } `); + + // Service and annotation names are the same + fs.writeJSON(join('webapp', 'manifest.json'), { + 'sap.app': { dataSources: { exisitingSerivce: { type: 'OData' } } } + }); + configCopy = cloneDeep(Object.assign({}, config, { name: 'aname', annotations: { name: 'aname' } })); + await enhanceData('', configCopy, fs); + expect(configCopy).toMatchInlineSnapshot(` + Object { + "annotations": Object { + "name": "aname_Annotation", + }, + "model": "", + "name": "aname", + "path": "/V2/Northwind/Northwind.svc/", + "previewSettings": Object { + "path": "/V2", + "url": "https://services.odata.org", + }, + "type": "edmx", + "url": "https://services.odata.org", + "version": "2", + } + `); + }); + }); +}); + +describe('remove', () => { + let fs: Editor; + beforeEach(async () => { + const ui5Yaml = (await UI5Config.newInstance('')) + .addFioriToolsProxydMiddleware({ ui5: {}, backend: [{ path: '/sap', url: 'https://localhost' }] }) + .toString(); + const ui5LocalYaml = (await UI5Config.newInstance('')) + .addFioriToolsProxydMiddleware({ ui5: {}, backend: [{ path: '/sap', url: 'https://localhost' }] }) + .addMockServerMiddleware( + [{ serviceName: 'mainService', servicePath: '/sap' }], + [ + { + urlPath: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/` + } + ] + ) + .toString(); + const ui5MockYaml = (await UI5Config.newInstance('')) + .addFioriToolsProxydMiddleware({ ui5: {}, backend: [{ path: '/sap', url: 'https://localhost' }] }) + .addMockServerMiddleware( + [{ serviceName: 'mainService', servicePath: '/sap' }], + [ + { + urlPath: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/` + } + ] + ) + .toString(); + // generate required files + fs = create(createStorage()); + fs.write(join(testDir, 'ui5.yaml'), ui5Yaml); + fs.write(join(testDir, 'ui5-local.yaml'), ui5LocalYaml); + fs.write(join(testDir, 'ui5-mock.yaml'), ui5MockYaml); + fs.writeJSON(join(testDir, 'package.json'), { ui5: { dependencies: [] } }); + fs.write( + join(testDir, 'webapp', 'manifest.json'), + JSON.stringify({ + 'sap.app': { + id: 'testappid', + dataSources: { + mainService: { + uri: '/sap', + type: 'OData', + settings: { + annotations: ['SEPMRA_PROD_MAN', 'annotation'], + localUri: 'annotations/annotation.xml' + } + }, + SEPMRA_PROD_MAN: { + uri: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/`, + type: 'ODataAnnotation', + settings: { + localUri: 'localService/SEPMRA_PROD_MAN.xml' + } + }, + annotation: { + type: 'ODataAnnotation', + uri: 'annotations/annotation.xml', + settings: { + localUri: 'annotations/annotation.xml' + } + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService' + } + } + } + }) + ); + }); + it('Try to remove an unexisting service', async () => { + await remove( + testDir, + { + name: 'dummyService', + url: 'https://dummyUrl', + path: '/dummyPath', + type: ServiceType.EDMX, + annotations: [{ technicalName: 'dummy-technical-name' }] as EdmxAnnotationsInfo[] + }, + fs + ); + // verify updated manifest.json + const manifest = fs.readJSON(join(testDir, 'webapp', 'manifest.json')) as any; + expect(manifest['sap.app'].dataSources).toStrictEqual({ + mainService: { + uri: '/sap', + type: 'OData', + settings: { + annotations: ['SEPMRA_PROD_MAN', 'annotation'], + localUri: 'annotations/annotation.xml' + } + }, + SEPMRA_PROD_MAN: { + uri: `/sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/`, + type: 'ODataAnnotation', + settings: { + localUri: 'localService/SEPMRA_PROD_MAN.xml' + } + }, + annotation: { + type: 'ODataAnnotation', + uri: 'annotations/annotation.xml', + settings: { + localUri: 'annotations/annotation.xml' + } + } + }); + expect(manifest['sap.ui5'].models).toStrictEqual({ + '': { + dataSource: 'mainService' + } + }); + // verify ui5.yaml, ui5-local.yaml, ui5-mock.yaml + expect(fs.read(join(testDir, 'ui5.yaml'))).toContain( + 'backend:\n - path: /sap\n url: https://localhost\n' + ); + expect(fs.read(join(testDir, 'ui5-local.yaml'))).toContain( + 'backend:\n - path: /sap\n url: https://localhost\n' + ); + expect(fs.read(join(testDir, 'ui5-mock.yaml'))).toContain('services:\n - urlPath: /sap\n'); + expect(fs.read(join(testDir, 'ui5-mock.yaml'))).toContain( + `annotations:\n - urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/\n` + ); + }); + it('Remove an existing service', async () => { + await remove( + testDir, + { + name: 'mainService', + url: 'https://localhost', + path: '/sap', + type: ServiceType.EDMX, + annotations: [ + { technicalName: 'SEPMRA_PROD_MAN', xml: '' } + ] as EdmxAnnotationsInfo[] + }, + fs + ); + // verify updated manifest.json + const manifest = fs.readJSON(join(testDir, 'webapp', 'manifest.json')) as any; + expect(manifest['sap.app'].dataSources).toStrictEqual({ + annotation: { + type: 'ODataAnnotation', + uri: 'annotations/annotation.xml', + settings: { + localUri: 'annotations/annotation.xml' + } + } }); + expect(manifest['sap.ui5'].models).toStrictEqual({}); + // verify ui5.yaml, ui5-local.yaml, ui5-mock.yaml + expect(fs.read(join(testDir, 'ui5.yaml'))).not.toContain('- path: /sap\n url: http://localhost\n'); + expect(fs.read(join(testDir, 'ui5-local.yaml'))).not.toContain( + '- path: /sap\n url: http://localhost\n' + ); + expect(fs.read(join(testDir, 'ui5-mock.yaml'))).not.toContain( + 'services:\n - urlPath: /sap/odata/testme\n ' + ); + expect(fs.read(join(testDir, 'ui5-mock.yaml'))).not.toContain( + `annotations:\n - urlPath: /sap/opu/odata/IWFND/CATALOGSERVICE;v=2/Annotations(TechnicalName='SEPMRA_PROD_MAN',Version='0001')/$value/\n` + ); }); }); diff --git a/packages/odata-service-writer/test/unit/updates.test.ts b/packages/odata-service-writer/test/unit/updates.test.ts index 75852d754e..c2cada8bee 100644 --- a/packages/odata-service-writer/test/unit/updates.test.ts +++ b/packages/odata-service-writer/test/unit/updates.test.ts @@ -7,6 +7,8 @@ import type { OdataService, CdsAnnotationsInfo } from '../../src'; import { OdataVersion, ServiceType } from '../../src'; import * as ejs from 'ejs'; import { expectedEdmxManifest } from '../test-data/manifest-json/edmx-manifest'; +import { expectedEdmxManifestMultipleAnnotations } from '../test-data/manifest-json/edmx-manifest-multiple-annotations'; +import { expectedEdmxManifestMultipleServices } from '../test-data/manifest-json/edmx-manifest-multiple-services'; import { expectedCdsManifest } from '../test-data/manifest-json/cap-manifest'; import type { Package } from '@sap-ux/project-access'; @@ -111,6 +113,83 @@ describe('updates', () => { expect(manifestJson).toEqual(expectedEdmxManifest); }); + test('Ensure manifest updates are updated as expected as in edmx projects with multiple annotations', () => { + const testManifest = { + 'sap.app': { + id: 'test.update.manifest' + } + }; + const service: OdataService = { + version: OdataVersion.v2, + client: '123', + model: 'amodel', + name: 'aname', + path: '/a/path', + type: ServiceType.EDMX, + annotations: [ + { + technicalName: 'annotation1Technical', + xml: 'annotation1xml', + name: 'annotation1' + }, + { + technicalName: 'annotation2Technical', + xml: 'annotation2xml', + name: 'annotation2' + } + ], + localAnnotationsName: 'localTest' + }; + + fs.writeJSON('./webapp/manifest.json', testManifest); + // Call updateManifest + updateManifest('./', service, fs, join(__dirname, '../../templates')); + const manifestJson = fs.readJSON('./webapp/manifest.json'); + expect(manifestJson).toEqual(expectedEdmxManifestMultipleAnnotations); + }); + + test('Ensure manifest updates are updated as expected as in edmx projects with multiple services', () => { + const testManifest = { + 'sap.app': { + id: 'test.update.manifest', + dataSources: { + 'mainService': { + type: 'OData' + } + } + }, + 'sap.ui5': { + models: { + '': { + dataSource: 'mainService' + } + } + } + }; + const service: OdataService = { + version: OdataVersion.v2, + client: '123', + model: 'amodel', + name: 'aname', + path: '/a/path', + type: ServiceType.EDMX, + annotations: [ + { + technicalName: 'annotation1Technical', + xml: 'annotation1xml', + name: 'annotation1' + } + ], + localAnnotationsName: 'localTest' + }; + + fs.writeJSON('./webapp/manifest.json', testManifest); + // Call updateManifest + updateManifest('./', service, fs, join(__dirname, '../../templates')); + const manifestJson = fs.readJSON('./webapp/manifest.json'); + expect(manifestJson).toEqual(expectedEdmxManifestMultipleServices); + }); + test('Ensure manifest updates are updated as expected as in cds projects', () => { const testManifest = { 'sap.app': { @@ -185,5 +264,31 @@ describe('updates', () => { const serviceCds = fs.read(serviceCdsPath); expect(serviceCds).toContain(`using from './annotations/annotations';`); }); + + it('writes annotation cds files correctly for multiple annotations', async () => { + const annotationsInfo: CdsAnnotationsInfo[] = [ + { + cdsFileContents: '"using AdminService as service from \'../../srv/admin-service\';"', + projectPath: 'testProject', + appPath: 'webapp', + projectName: 'annotations' + }, + { + cdsFileContents: '"using IncidentService as service from \'../../srv/incidentservice\';"', + projectPath: 'testProject', + appPath: 'webapp', + projectName: 'annotations' + } + ]; + const annotationsPath = join('./testProject/webapp/annotations', 'annotations.cds'); + await updateCdsFilesWithAnnotations(annotationsInfo, fs); + const annotationCds = fs.read(annotationsPath); + expect(annotationCds).toContain(annotationsInfo[0].cdsFileContents); + expect(annotationCds).toContain(annotationsInfo[1].cdsFileContents); + // Convert the annotation path to the services path + const serviceCdsPath = path.join(path.dirname(annotationsPath).replace('annotations', ''), 'services.cds'); + const serviceCds = fs.read(serviceCdsPath); + expect(serviceCds).toContain(`using from './annotations/annotations';`); + }); }); }); diff --git a/packages/ui5-config/src/index.ts b/packages/ui5-config/src/index.ts index 0d198f2289..e25e2dccec 100644 --- a/packages/ui5-config/src/index.ts +++ b/packages/ui5-config/src/index.ts @@ -1,5 +1,6 @@ export { UI5Config } from './ui5config'; export { + DataSourceConfig, Configuration, CustomItem, CustomTask, diff --git a/packages/ui5-config/src/middlewares.ts b/packages/ui5-config/src/middlewares.ts index 375f153cf5..e34b57130d 100644 --- a/packages/ui5-config/src/middlewares.ts +++ b/packages/ui5-config/src/middlewares.ts @@ -5,7 +5,8 @@ import type { FioriToolsProxyConfig, MockserverConfig, FioriToolsProxyConfigUI5, - FioriPreviewConfig + FioriPreviewConfig, + DataSourceConfig } from './types'; import type { NodeComment } from '@sap-ux/yaml'; @@ -128,23 +129,30 @@ export function getFioriToolsProxyMiddlewareConfig( } export const getMockServerMiddlewareConfig = ( - path?: string, - annotationsConfig: MockserverConfig['annotations'] = [] + dataSourcesConfig: DataSourceConfig[], + annotationsConfig: MockserverConfig['annotations'], + appRoot?: string ): CustomMiddleware => { - path = path?.replace(/\/$/, ''); // Mockserver is sensitive to trailing '/' + const services: MockserverConfig['services'] = []; + + // Populate services based on dataSourcesConfig + dataSourcesConfig.forEach((dataSource) => { + const serviceRoot = `${appRoot ?? './webapp'}/localService/${dataSource.serviceName}`; + + const newServiceData = { + urlPath: dataSource.servicePath.replace(/\/$/, ''), // Mockserver is sensitive to trailing '/' + metadataPath: dataSource.metadataPath ?? `${serviceRoot}/metadata.xml`, + mockdataPath: `${serviceRoot}/data`, + generateMockData: true + }; + services.push(newServiceData); + }); return { name: 'sap-fe-mockserver', beforeMiddleware: 'csp', configuration: { mountPath: '/', - services: [ - { - urlPath: path ?? '', - metadataPath: './webapp/localService/metadata.xml', - mockdataPath: './webapp/localService/data', - generateMockData: true - } - ], + services, annotations: annotationsConfig } }; diff --git a/packages/ui5-config/src/types/index.ts b/packages/ui5-config/src/types/index.ts index 364666faad..e6e42855a7 100644 --- a/packages/ui5-config/src/types/index.ts +++ b/packages/ui5-config/src/types/index.ts @@ -63,3 +63,5 @@ export interface ServeStaticPath { src: string; fallthrough: boolean; } + +export type DataSourceConfig = { serviceName: string; servicePath: string; metadataPath?: string }; diff --git a/packages/ui5-config/src/ui5config.ts b/packages/ui5-config/src/ui5config.ts index 34964d4a80..c637bebbc3 100644 --- a/packages/ui5-config/src/ui5config.ts +++ b/packages/ui5-config/src/ui5config.ts @@ -13,6 +13,7 @@ import type { Adp, MockserverConfig, ServeStaticPath, + DataSourceConfig, AbapDeployConfig } from './types'; import type { NodeComment, YAMLMap, YAMLSeq } from '@sap-ux/yaml'; @@ -268,7 +269,7 @@ export class UI5Config { } /** - * Adds a backend configuration to an existing fiori-tools-proxy middleware. If the config does not contain a fiori-tools-proxy middleware, an error is thrown. + * Adds a backend configuration to an existing fiori-tools-proxy middleware keeping any existing 'fiori-tools-proxy' backend configurations. If the config does not contain a fiori-tools-proxy middleware, an error is thrown. * * @param backend config of backend that is to be proxied * @returns {UI5Config} the UI5Config instance @@ -281,12 +282,59 @@ export class UI5Config { throw new Error('Could not find fiori-tools-proxy'); } const comments = getBackendComments(backend); - const backendNode = this.document.createNode({ value: backend, comments }); - - this.document - .getMap({ start: proxyMiddleware as YAMLMap, path: 'configuration' }) - .set('backend', [backendNode]); + let backendNode; + const proxyMiddlewareYamlContent = this.findCustomMiddleware(fioriToolsProxy); + const proxyMiddlewareConfig = proxyMiddlewareYamlContent?.configuration; + // Add new entry to existing backend configurations in yaml + if (proxyMiddlewareConfig?.backend) { + // Avoid adding duplicates by checking existing backend configs + if (!proxyMiddlewareConfig.backend.find((existingBackend) => existingBackend.url === backend.url)) { + backendNode = this.document.createNode({ + value: backend, + comments + }); + const configuration = this.document.getMap({ + start: proxyMiddleware as YAMLMap, + path: 'configuration' + }); + const backendConfigs = this.document.getSequence({ start: configuration, path: 'backend' }); + if (backendConfigs.items.length === 0) { + configuration.set('backend', [backendNode]); + } else { + backendConfigs.add(backendNode); + } + } + } else { + // Create a new 'backend' node in yaml for middleware config + backendNode = this.document.createNode({ value: backend, comments }); + this.document + .getMap({ start: proxyMiddleware as YAMLMap, path: 'configuration' }) + .set('backend', [backendNode]); + } + return this; + } + /** + * Removes a backend configuration from an existing fiori-tools-proxy middleware backend configurations. If the config does not contain a fiori-tools-proxy middleware, an error is thrown. + * + * @param backendUrl url of the backend to delete. + * @returns {UI5Config} the UI5Config instance + * @memberof UI5Config + */ + public removeBackendFromFioriToolsProxydMiddleware(backendUrl: string): this { + const fioriToolsProxyMiddleware = this.findCustomMiddleware(fioriToolsProxy); + if (!fioriToolsProxyMiddleware) { + throw new Error('Could not find fiori-tools-proxy'); + } else { + const proxyMiddlewareConfig = fioriToolsProxyMiddleware?.configuration; + // Remove backend from middleware configurations in yaml + if (proxyMiddlewareConfig?.backend) { + proxyMiddlewareConfig.backend = proxyMiddlewareConfig.backend.filter( + (existingBackend) => existingBackend.url !== backendUrl + ); + this.updateCustomMiddleware(fioriToolsProxyMiddleware); + } + } return this; } @@ -335,19 +383,113 @@ export class UI5Config { /** * Adds a instance of the mockserver middleware to the config. * - * @param path option path that is to be mocked - * @param annotationsConfig optional annotations config that is to be mocked + * @param dataSourcesConfig - annotations config that is to be mocked + * @param annotationsConfig - annotations config that is to be mocked + * @param appRoot - root to the application * @returns {UI5Config} the UI5Config instance * @memberof UI5Config */ - public addMockServerMiddleware(path?: string, annotationsConfig?: MockserverConfig['annotations']): this { + public addMockServerMiddleware( + dataSourcesConfig: DataSourceConfig[], + annotationsConfig: MockserverConfig['annotations'], + appRoot?: string + ): this { this.document.appendTo({ path: 'server.customMiddleware', - value: getMockServerMiddlewareConfig(path, annotationsConfig) + value: getMockServerMiddlewareConfig(dataSourcesConfig, annotationsConfig, appRoot) }); return this; } + /** + * Adds a service configuration to an existing sap-fe-mockserver middleware keeping any existing service configurations. If the config does not contain a sap-fe-mockserver middleware, an error is thrown. + * + * @param dataSourceConfig - dataSource config from manifest to add to mockserver middleware services list + * @param appRoot - root to the application + * @param annotationsConfig - optional, annotations config that is to be mocked + * @returns {UI5Config} the UI5Config instance + * @memberof UI5Config + */ + public addServiceToMockserverMiddleware( + dataSourceConfig: DataSourceConfig, + appRoot?: string, + annotationsConfig: MockserverConfig['annotations'] = [] + ): this { + const mockserverMiddleware = this.findCustomMiddleware('sap-fe-mockserver'); + if (!mockserverMiddleware) { + throw new Error('Could not find sap-fe-mockserver'); + } else { + // Else append new data to current middleware config and then run middleware update + const serviceRoot = `${appRoot ?? './webapp'}/localService/${dataSourceConfig.serviceName}`; + + const mockserverMiddlewareConfig = mockserverMiddleware?.configuration; + if (mockserverMiddlewareConfig?.services) { + const urlPath = dataSourceConfig.servicePath.replace(/\/$/, ''); // Mockserver is sensitive to trailing '/' + const newServiceData = { + urlPath, + metadataPath: dataSourceConfig.metadataPath ?? `${serviceRoot}/metadata.xml`, + mockdataPath: `${serviceRoot}/data`, + generateMockData: true + }; + const serviceIndex = mockserverMiddlewareConfig.services.findIndex( + (existingService) => existingService.urlPath === urlPath + ); + if (serviceIndex === -1) { + mockserverMiddlewareConfig.services = [...mockserverMiddlewareConfig.services, newServiceData]; + } + } + if (mockserverMiddlewareConfig?.annotations) { + const existingAnnotations = mockserverMiddlewareConfig.annotations; + annotationsConfig.forEach((annotationConfig) => { + if ( + !existingAnnotations.find( + (existingAnnotation) => existingAnnotation.urlPath === annotationConfig.urlPath + ) + ) { + existingAnnotations.push(annotationConfig); + } + }); + } + this.updateCustomMiddleware(mockserverMiddleware); + } + return this; + } + + /** + * Removes a service from the mockserver middleware. + * + * @param servicePath - path of the service that is to be deleted + * @param annotationPaths - paths of the service related annotations + * @returns {UI5Config} the UI5Config instance + * @memberof UI5Config + */ + public removeServiceFromMockServerMiddleware(servicePath: string, annotationPaths: string[]): this { + const mockserverMiddleware = this.findCustomMiddleware('sap-fe-mockserver'); + if (!mockserverMiddleware) { + throw new Error('Could not find sap-fe-mockserver'); + } else { + const mockserverMiddlewareConfig = mockserverMiddleware?.configuration; + // Remove service from middleware configurations in yaml + if (mockserverMiddlewareConfig?.services) { + mockserverMiddlewareConfig.services = mockserverMiddlewareConfig?.services.filter( + (existingService) => existingService.urlPath !== servicePath.replace(/\/$/, '') + ); + } + // Remove service related annotations + if (mockserverMiddlewareConfig?.annotations) { + const mockserverMiddlewareConfigAnnotations = mockserverMiddlewareConfig.annotations; + annotationPaths.forEach((annotationPath: string) => { + // Search for annotations that needs to be deleted + mockserverMiddlewareConfig.annotations = mockserverMiddlewareConfigAnnotations.filter( + (existingAnnotation) => existingAnnotation.urlPath !== annotationPath + ); + }); + } + this.updateCustomMiddleware(mockserverMiddleware); + } + return this; + } + /** * Adds the ABAP deployment task to the config. * diff --git a/packages/ui5-config/test/__snapshots__/index.test.ts.snap b/packages/ui5-config/test/__snapshots__/index.test.ts.snap index 1b454051ba..2deca3ec70 100644 --- a/packages/ui5-config/test/__snapshots__/index.test.ts.snap +++ b/packages/ui5-config/test/__snapshots__/index.test.ts.snap @@ -387,7 +387,7 @@ exports[`UI5Config addFioriToolsProxydMiddleware add without backend or but all " `; -exports[`UI5Config addMockServerMiddleware add with given path 1`] = ` +exports[`UI5Config addMockServerMiddleware add with services 1`] = ` "server: customMiddleware: - name: sap-fe-mockserver @@ -395,15 +395,15 @@ exports[`UI5Config addMockServerMiddleware add with given path 1`] = ` configuration: mountPath: / services: - - urlPath: /~testpath~ - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /path/to/service + metadataPath: ./webapp/localService/new-service/metadata.xml + mockdataPath: ./webapp/localService/new-service/data generateMockData: true annotations: [] " `; -exports[`UI5Config addMockServerMiddleware add with path and annotationsConfig 1`] = ` +exports[`UI5Config addMockServerMiddleware add with services and annotations 1`] = ` "server: customMiddleware: - name: sap-fe-mockserver @@ -411,9 +411,9 @@ exports[`UI5Config addMockServerMiddleware add with path and annotationsConfig 1 configuration: mountPath: / services: - - urlPath: /~testpath~ - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /path/to/service + metadataPath: ./webapp/localService/new-service/metadata.xml + mockdataPath: ./webapp/localService/new-service/data generateMockData: true annotations: - localPath: ./webapp/annotations/annotations.xml @@ -421,7 +421,7 @@ exports[`UI5Config addMockServerMiddleware add with path and annotationsConfig 1 " `; -exports[`UI5Config addMockServerMiddleware add without path 1`] = ` +exports[`UI5Config addMockServerMiddleware add with services and appRoot 1`] = ` "server: customMiddleware: - name: sap-fe-mockserver @@ -429,14 +429,26 @@ exports[`UI5Config addMockServerMiddleware add without path 1`] = ` configuration: mountPath: / services: - - urlPath: '' - metadataPath: ./webapp/localService/metadata.xml - mockdataPath: ./webapp/localService/data + - urlPath: /path/to/service + metadataPath: ./appRoot/webapp/localService/new-service/metadata.xml + mockdataPath: ./appRoot/webapp/localService/new-service/data generateMockData: true annotations: [] " `; +exports[`UI5Config addMockServerMiddleware add without services and annotations 1`] = ` +"server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: [] + annotations: [] +" +`; + exports[`UI5Config addServeStaticConfig add with multiple paths (existing config) 1`] = ` "server: customMiddleware: @@ -520,6 +532,72 @@ exports[`UI5Config addServeStaticConfig update serve static config 1`] = ` " `; +exports[`UI5Config addServiceToMockserverMiddleware add new service 1`] = ` +"server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: + - urlPath: /path/to/service + metadataPath: ./webapp/localService/new-service/metadata.xml + mockdataPath: ./webapp/localService/new-service/data + generateMockData: true + annotations: [] +" +`; + +exports[`UI5Config addServiceToMockserverMiddleware add new service with annotationsConfig 1`] = ` +"server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: + - urlPath: /path/to/service + metadataPath: ./webapp/localService/new-service/metadata.xml + mockdataPath: ./webapp/localService/new-service/data + generateMockData: true + annotations: + - localPath: ./webapp/annotations/annotations.xml + urlPath: annotations.xml +" +`; + +exports[`UI5Config addServiceToMockserverMiddleware add new service with appRoot 1`] = ` +"server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: + - urlPath: /path/to/service + metadataPath: ./appRoot/localService/new-service/metadata.xml + mockdataPath: ./appRoot/localService/new-service/data + generateMockData: true + annotations: [] +" +`; + +exports[`UI5Config addServiceToMockserverMiddleware try to add service duplicate 1`] = ` +"server: + customMiddleware: + - name: sap-fe-mockserver + beforeMiddleware: csp + configuration: + mountPath: / + services: + - urlPath: /path/to/service + metadataPath: ./webapp/localService/new-service/metadata.xml + mockdataPath: ./webapp/localService/new-service/data + generateMockData: true + annotations: [] +" +`; + exports[`UI5Config addUI5Framework Add with specific theme and additional library 1`] = ` "framework: name: SAPUI5 diff --git a/packages/ui5-config/test/index.test.ts b/packages/ui5-config/test/index.test.ts index 545035dd69..85167a975b 100644 --- a/packages/ui5-config/test/index.test.ts +++ b/packages/ui5-config/test/index.test.ts @@ -1,10 +1,12 @@ import { AuthenticationType } from '@sap-ux/store'; -import type { BspApp, UI5ProxyConfig } from '../src'; +import type { BspApp, FioriToolsProxyConfig, UI5ProxyConfig } from '../src'; import { UI5Config } from '../src'; +import { fioriToolsProxy } from '../src/constants'; describe('UI5Config', () => { // values for testing const path = '/~testpath~', + name = 'mainService', url = 'http://localhost:8080', destination = '~destination~', destinationInstance = '~destinationInstance~', @@ -221,6 +223,28 @@ describe('UI5Config', () => { expect(ui5Config.toString()).toMatchSnapshot(); }); + test('should not add duplicate', () => { + ui5Config.addFioriToolsProxydMiddleware({ ui5: {} }); + // Add backend + ui5Config.addBackendToFioriToolsProxydMiddleware({ + url, + path + }); + // Try to add same backend + ui5Config.addBackendToFioriToolsProxydMiddleware({ + url, + path + }); + const fioriToolsProxyMiddlewareConfig = + ui5Config.findCustomMiddleware(fioriToolsProxy)?.configuration; + expect(fioriToolsProxyMiddlewareConfig?.backend).toStrictEqual([ + { + path: '/~testpath~', + url: 'http://localhost:8080' + } + ]); + }); + test('should add comments with backend authentication type as reentrance ticket', () => { ui5Config.addFioriToolsProxydMiddleware({ ui5: {} }); ui5Config.addBackendToFioriToolsProxydMiddleware({ @@ -254,6 +278,58 @@ describe('UI5Config', () => { }); }); + describe('removeBackendFromFioriToolsProxydMiddleware', () => { + test('add proxy with backend first and then call remove backend for existing backend', () => { + ui5Config.addFioriToolsProxydMiddleware({ ui5: {}, backend: [{ url, path }] }); + let fioriToolsProxyMiddleware = ui5Config.findCustomMiddleware('fiori-tools-proxy'); + expect(fioriToolsProxyMiddleware?.configuration).toStrictEqual({ + ignoreCertError: false, + backend: [ + { + url: 'http://localhost:8080', + path: '/~testpath~' + } + ], + ui5: { path: ['/resources', '/test-resources'], url: 'https://ui5.sap.com' } + }); + ui5Config.removeBackendFromFioriToolsProxydMiddleware(url); + fioriToolsProxyMiddleware = ui5Config.findCustomMiddleware('fiori-tools-proxy'); + expect(fioriToolsProxyMiddleware?.configuration).toStrictEqual({ + ignoreCertError: false, + backend: [], + ui5: { path: ['/resources', '/test-resources'], url: 'https://ui5.sap.com' } + }); + }); + + test('add proxy with backend first and then call remove backend for unexisting backend', () => { + ui5Config.addFioriToolsProxydMiddleware({ ui5: {}, backend: [{ url, path }] }); + const initialFioriToolsProxyMiddleware = + ui5Config.findCustomMiddleware('fiori-tools-proxy'); + expect(initialFioriToolsProxyMiddleware?.configuration).toStrictEqual({ + ignoreCertError: false, + backend: [ + { + url: 'http://localhost:8080', + path: '/~testpath~' + } + ], + ui5: { path: ['/resources', '/test-resources'], url: 'https://ui5.sap.com' } + }); + ui5Config.removeBackendFromFioriToolsProxydMiddleware('dummy'); + const updatedFioriToolsProxyMiddleware = + ui5Config.findCustomMiddleware('fiori-tools-proxy'); + // Check if nothing changed + expect(initialFioriToolsProxyMiddleware?.configuration).toStrictEqual( + updatedFioriToolsProxyMiddleware?.configuration + ); + }); + + test('try removing backend without a proxy middleware added before', () => { + ui5Config.addFioriToolsAppReloadMiddleware(); + expect(() => ui5Config.removeBackendFromFioriToolsProxydMiddleware(url)).toThrowError(); + }); + }); + describe('addUi5ToFioriToolsProxydMiddleware', () => { test('add ui5 config to empty tools middleware config', () => { ui5Config.addFioriToolsProxydMiddleware({}); @@ -266,19 +342,146 @@ describe('UI5Config', () => { }); describe('addMockServerMiddleware', () => { - test('add with given path', () => { - ui5Config.addMockServerMiddleware(path); + test('add without services and annotations', () => { + ui5Config.addMockServerMiddleware([], []); expect(ui5Config.toString()).toMatchSnapshot(); }); - test('add without path', () => { - ui5Config.addMockServerMiddleware(); + test('add with services', () => { + ui5Config.addMockServerMiddleware([{ serviceName: 'new-service', servicePath: '/path/to/service' }], []); expect(ui5Config.toString()).toMatchSnapshot(); }); - test('add with path and annotationsConfig', () => { - ui5Config.addMockServerMiddleware(path, annotationsConfig); + + test('add with services and appRoot', () => { + ui5Config.addMockServerMiddleware( + [{ serviceName: 'new-service', servicePath: '/path/to/service' }], + [], + './appRoot/webapp' + ); expect(ui5Config.toString()).toMatchSnapshot(); }); + + test('add with services and annotations', () => { + ui5Config.addMockServerMiddleware( + [{ serviceName: 'new-service', servicePath: '/path/to/service' }], + annotationsConfig + ); + expect(ui5Config.toString()).toMatchSnapshot(); + }); + }); + + describe('addServiceToMockserverMiddleware', () => { + test('add new service', () => { + ui5Config.addMockServerMiddleware([], []); + ui5Config.addServiceToMockserverMiddleware({ serviceName: 'new-service', servicePath: '/path/to/service' }); + expect(ui5Config.toString()).toMatchSnapshot(); + }); + + test('add new service with appRoot', () => { + ui5Config.addMockServerMiddleware([], []); + ui5Config.addServiceToMockserverMiddleware( + { serviceName: 'new-service', servicePath: '/path/to/service' }, + './appRoot' + ); + expect(ui5Config.toString()).toMatchSnapshot(); + }); + + test('try to add service duplicate', () => { + ui5Config.addMockServerMiddleware([], []); + ui5Config.addServiceToMockserverMiddleware({ serviceName: 'new-service', servicePath: '/path/to/service' }); + ui5Config.addServiceToMockserverMiddleware({ serviceName: 'new-service', servicePath: '/path/to/service' }); + expect(ui5Config.toString()).toMatchSnapshot(); + }); + + test('add new service with annotationsConfig', () => { + ui5Config.addMockServerMiddleware([], []); + ui5Config.addServiceToMockserverMiddleware( + { serviceName: 'new-service', servicePath: '/path/to/service' }, + undefined, + annotationsConfig + ); + expect(ui5Config.toString()).toMatchSnapshot(); + }); + }); + + describe('removeServiceFromMockServerMiddleware', () => { + test('remove exisisting service', () => { + // Create middleware with one service + ui5Config.addMockServerMiddleware([{ serviceName: 'new-service', servicePath: '/path/to/service' }], []); + let mockserverMiddleware = ui5Config.findCustomMiddleware('sap-fe-mockserver'); + expect(mockserverMiddleware?.configuration).toStrictEqual({ + services: [ + { + generateMockData: true, + metadataPath: './webapp/localService/new-service/metadata.xml', + mockdataPath: './webapp/localService/new-service/data', + urlPath: '/path/to/service' + } + ], + mountPath: '/', + annotations: [] + }); + // Remove it + ui5Config.removeServiceFromMockServerMiddleware('/path/to/service', []); + mockserverMiddleware = ui5Config.findCustomMiddleware('sap-fe-mockserver'); + expect(mockserverMiddleware?.configuration).toStrictEqual({ + services: [], + mountPath: '/', + annotations: [] + }); + }); + + test('remove exisisting service with annotations', () => { + // Create middleware with one service + ui5Config.addMockServerMiddleware( + [{ serviceName: 'new-service', servicePath: '/path/to/service' }], + [{ urlPath: '/path/to/annotation' }] + ); + let mockserverMiddleware = ui5Config.findCustomMiddleware('sap-fe-mockserver'); + expect(mockserverMiddleware?.configuration).toStrictEqual({ + services: [ + { + generateMockData: true, + metadataPath: './webapp/localService/new-service/metadata.xml', + mockdataPath: './webapp/localService/new-service/data', + urlPath: '/path/to/service' + } + ], + mountPath: '/', + annotations: [ + { + urlPath: '/path/to/annotation' + } + ] + }); + // Remove it + ui5Config.removeServiceFromMockServerMiddleware('/path/to/service', ['/path/to/annotation']); + mockserverMiddleware = ui5Config.findCustomMiddleware('sap-fe-mockserver'); + expect(mockserverMiddleware?.configuration).toStrictEqual({ + services: [], + mountPath: '/', + annotations: [] + }); + }); + + test('remove unexisting service', () => { + // Create middleware without any services + ui5Config.addMockServerMiddleware([], []); + let mockserverMiddleware = ui5Config.findCustomMiddleware('sap-fe-mockserver'); + expect(mockserverMiddleware?.configuration).toStrictEqual({ + services: [], + mountPath: '/', + annotations: [] + }); + // Try to remove unexisting service + ui5Config.removeServiceFromMockServerMiddleware('/path/to/service', []); + mockserverMiddleware = ui5Config.findCustomMiddleware('sap-fe-mockserver'); + expect(mockserverMiddleware?.configuration).toStrictEqual({ + services: [], + mountPath: '/', + annotations: [] + }); + }); }); test('getAppReloadMiddlewareConfig', () => {