diff --git a/collections/bff/catalog/Create a new EService.bru b/collections/bff/catalog/Create a new EService.bru index 840278bf98..65c500cf01 100644 --- a/collections/bff/catalog/Create a new EService.bru +++ b/collections/bff/catalog/Create a new EService.bru @@ -22,7 +22,8 @@ body:json { "technology": "SOAP", "mode": "DELIVER", "isSignalHubEnabled": true, - "isDelegable": true + "isDelegable": true, + "isClientAccessDelegable": true } } diff --git a/collections/bff/catalog/Update EService general information.bru b/collections/bff/catalog/Update EService general information.bru index 9122bdc17b..ebcf9d0100 100644 --- a/collections/bff/catalog/Update EService general information.bru +++ b/collections/bff/catalog/Update EService general information.bru @@ -26,7 +26,8 @@ body:json { "technology": "SOAP", "mode": "RECEIVE", "isSignalHubEnabled": false, - "isDelegable": false + "isDelegable": false, + "isClientAccessDelegable": false } } diff --git a/packages/api-clients/open-api/apiGatewayApi.yml b/packages/api-clients/open-api/apiGatewayApi.yml index 414548b461..6950c59876 100644 --- a/packages/api-clients/open-api/apiGatewayApi.yml +++ b/packages/api-clients/open-api/apiGatewayApi.yml @@ -2621,6 +2621,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean EServiceAttributes: description: the attributes set associated to the EService properties: diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index a0c57daa2d..2581a5033a 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -13096,6 +13096,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean EServiceSeed: type: object additionalProperties: false @@ -13117,6 +13119,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean UpdateEServiceDescriptorQuotas: required: - voucherLifespan @@ -13366,6 +13370,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean ProducerEServiceDetails: type: object additionalProperties: false @@ -13396,6 +13402,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean EServiceMode: type: string description: Risk Analysis Mode @@ -13525,6 +13533,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean EServiceDoc: type: object additionalProperties: false diff --git a/packages/api-clients/open-api/catalogApi.yml b/packages/api-clients/open-api/catalogApi.yml index 98478835d0..61d5e552cf 100644 --- a/packages/api-clients/open-api/catalogApi.yml +++ b/packages/api-clients/open-api/catalogApi.yml @@ -1130,6 +1130,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean UpdateEServiceSeed: type: object additionalProperties: false @@ -1155,6 +1157,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean DescriptorSeedForEServiceCreation: required: - audience @@ -1465,6 +1469,8 @@ components: type: boolean isDelegable: type: boolean + isClientAccessDelegable: + type: boolean EServiceMode: type: string description: Risk Analysis Mode diff --git a/packages/api-gateway/src/services/catalogService.ts b/packages/api-gateway/src/services/catalogService.ts index 8765e5e934..ef05695232 100644 --- a/packages/api-gateway/src/services/catalogService.ts +++ b/packages/api-gateway/src/services/catalogService.ts @@ -256,5 +256,6 @@ export async function enhanceEservice( producer: producerOrganization, isSignalHubEnabled: eservice.isSignalHubEnabled, isDelegable: eservice.isDelegable, + isClientAccessDelegable: eservice.isClientAccessDelegable, }; } diff --git a/packages/backend-for-frontend/src/api/catalogApiConverter.ts b/packages/backend-for-frontend/src/api/catalogApiConverter.ts index af0c8f6df3..1ca201b043 100644 --- a/packages/backend-for-frontend/src/api/catalogApiConverter.ts +++ b/packages/backend-for-frontend/src/api/catalogApiConverter.ts @@ -120,6 +120,7 @@ export function toBffCatalogDescriptorEService( ), isSignalHubEnabled: eservice.isSignalHubEnabled, isDelegable: eservice.isDelegable, + isClientAccessDelegable: eservice.isClientAccessDelegable, }; } @@ -267,6 +268,7 @@ export function toBffCatalogApiProducerDescriptorEService( descriptors: notDraftDecriptors, isSignalHubEnabled: eservice.isSignalHubEnabled, isDelegable: eservice.isDelegable, + isClientAccessDelegable: eservice.isClientAccessDelegable, }; } diff --git a/packages/backend-for-frontend/src/model/types.ts b/packages/backend-for-frontend/src/model/types.ts index 5440dcff86..1c8c198f9e 100644 --- a/packages/backend-for-frontend/src/model/types.ts +++ b/packages/backend-for-frontend/src/model/types.ts @@ -209,5 +209,6 @@ export const ConfigurationEservice = z.object({ riskAnalysis: z.array(ConfigurationRiskAnalysis), isSignalHubEnabled: z.boolean().optional(), isDelegable: z.boolean().optional(), + isClientAccessDelegable: z.boolean().optional(), }); export type ConfigurationEservice = z.infer; diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index a33f8a9e24..749717bd76 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -327,6 +327,7 @@ export function catalogServiceBuilder( ), isSignalHubEnabled: eservice.isSignalHubEnabled, isDelegable: eservice.isDelegable, + isClientAccessDelegable: eservice.isClientAccessDelegable, }; }, updateEServiceDescription: async ( @@ -1133,6 +1134,7 @@ export function catalogServiceBuilder( }, isSignalHubEnabled: importedEservice.isSignalHubEnabled, isDelegable: importedEservice.isDelegable, + isClientAccessDelegable: importedEservice.isClientAccessDelegable, }; const pollEServiceById = createPollingByCondition(() => diff --git a/packages/backend-for-frontend/src/utilities/fileUtils.ts b/packages/backend-for-frontend/src/utilities/fileUtils.ts index c5fe21a36d..19849ae58a 100644 --- a/packages/backend-for-frontend/src/utilities/fileUtils.ts +++ b/packages/backend-for-frontend/src/utilities/fileUtils.ts @@ -82,6 +82,7 @@ export function buildJsonConfig( mode: eservice.mode, isSignalHubEnabled: eservice.isSignalHubEnabled, isDelegable: eservice.isDelegable, + isClientAccessDelegable: eservice.isClientAccessDelegable, descriptor: { interface: descriptor.interface && { prettyName: descriptor.interface.prettyName, diff --git a/packages/catalog-process/src/model/domain/apiConverter.ts b/packages/catalog-process/src/model/domain/apiConverter.ts index 075efa5734..110bac9d96 100644 --- a/packages/catalog-process/src/model/domain/apiConverter.ts +++ b/packages/catalog-process/src/model/domain/apiConverter.ts @@ -197,4 +197,5 @@ export const eServiceToApiEService = ( descriptors: eservice.descriptors.map(descriptorToApiDescriptor), isSignalHubEnabled: eservice.isSignalHubEnabled, isDelegable: eservice.isDelegable, + isClientAccessDelegable: eservice.isClientAccessDelegable, }); diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 8afcb21b34..dd992febb9 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -37,7 +37,7 @@ import { eserviceMode, } from "pagopa-interop-models"; import { catalogApi } from "pagopa-interop-api-clients"; -import { match } from "ts-pattern"; +import { match, P } from "ts-pattern"; import { apiAgreementApprovalPolicyToAgreementApprovalPolicy, apiEServiceModeToEServiceMode, @@ -438,6 +438,11 @@ export function catalogServiceBuilder( riskAnalysis: [], isSignalHubEnabled: seed.isSignalHubEnabled, isDelegable: seed.isDelegable, + isClientAccessDelegable: match(seed.isDelegable) + .with(P.nullish, () => undefined) + .with(false, () => false) + .with(true, () => seed.isClientAccessDelegable) + .exhaustive(), }; const eserviceCreationEvent = toCreateEventEServiceAdded( @@ -560,6 +565,11 @@ export function catalogServiceBuilder( : eservice.data.descriptors, isSignalHubEnabled: eserviceSeed.isSignalHubEnabled, isDelegable: eserviceSeed.isDelegable, + isClientAccessDelegable: match(eserviceSeed.isDelegable) + .with(P.nullish, () => undefined) + .with(false, () => false) + .with(true, () => eserviceSeed.isClientAccessDelegable) + .exhaustive(), }; const event = toCreateEventEServiceUpdated( diff --git a/packages/catalog-process/test/createEService.test.ts b/packages/catalog-process/test/createEService.test.ts index ce33e21c05..a7399a7f28 100644 --- a/packages/catalog-process/test/createEService.test.ts +++ b/packages/catalog-process/test/createEService.test.ts @@ -14,6 +14,7 @@ import { generateId, } from "pagopa-interop-models"; import { expect, describe, it, beforeAll, vi, afterAll } from "vitest"; +import { match } from "ts-pattern"; import { eServiceDuplicate, inconsistentDailyCalls, @@ -42,6 +43,11 @@ describe("create eservice", () => { it("should write on event-store for the creation of an eservice", async () => { const isSignalHubEnabled = randomArrayItem([false, true, undefined]); const isDelegable = randomArrayItem([false, true, undefined]); + const isClientAccessDelegable = match(isDelegable) + .with(undefined, () => undefined) + .with(true, () => randomArrayItem([false, true, undefined])) + .with(false, () => false) + .exhaustive(); const eservice = await catalogService.createEService( { @@ -52,6 +58,7 @@ describe("create eservice", () => { descriptor: buildDescriptorSeedForEserviceCreation(mockDescriptor), isSignalHubEnabled, isDelegable, + isClientAccessDelegable, }, { authData: getMockAuthData(mockEService.producerId), @@ -100,6 +107,7 @@ describe("create eservice", () => { descriptors: [], isSignalHubEnabled, isDelegable, + isClientAccessDelegable, }; const expectedEserviceWithDescriptor: EService = { ...mockEService, @@ -107,6 +115,101 @@ describe("create eservice", () => { id: eservice.id, isSignalHubEnabled, isDelegable, + isClientAccessDelegable, + descriptors: [ + { + ...mockDescriptor, + id: eservice.descriptors[0].id, + createdAt: new Date(), + serverUrls: [], + }, + ], + }; + + expect(eserviceCreationPayload.eservice).toEqual( + toEServiceV2(expectedEservice) + ); + expect(descriptorCreationPayload.eservice).toEqual( + toEServiceV2(expectedEserviceWithDescriptor) + ); + }); + + it("should create an eservice correctly handling isClientAccessDelegable when isDelegable is not true", async () => { + const isSignalHubEnabled = randomArrayItem([false, true, undefined]); + const isDelegable: false | undefined = randomArrayItem([false, undefined]); + const isClientAccessDelegable = randomArrayItem([false, true, undefined]); + const expectedIsClientAccessDelegable = match(isDelegable) + .with(false, () => false) + .with(undefined, () => undefined) + .exhaustive(); + + const eservice = await catalogService.createEService( + { + name: mockEService.name, + description: mockEService.description, + technology: "REST", + mode: "DELIVER", + descriptor: buildDescriptorSeedForEserviceCreation(mockDescriptor), + isSignalHubEnabled, + isDelegable, + isClientAccessDelegable, + }, + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + expect(eservice).toBeDefined(); + + const eserviceCreationEvent = await readEventByStreamIdAndVersion( + eservice.id, + 0, + "catalog", + postgresDB + ); + const descriptorCreationEvent = await readLastEserviceEvent(eservice.id); + + expect(eserviceCreationEvent).toMatchObject({ + stream_id: eservice.id, + version: "0", + type: "EServiceAdded", + event_version: 2, + }); + expect(descriptorCreationEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorAdded", + event_version: 2, + }); + + const eserviceCreationPayload = decodeProtobufPayload({ + messageType: EServiceAddedV2, + payload: eserviceCreationEvent.data, + }); + const descriptorCreationPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorAddedV2, + payload: descriptorCreationEvent.data, + }); + + const expectedEservice: EService = { + ...mockEService, + createdAt: new Date(), + id: eservice.id, + descriptors: [], + isSignalHubEnabled, + isDelegable, + isClientAccessDelegable: expectedIsClientAccessDelegable, + }; + const expectedEserviceWithDescriptor: EService = { + ...mockEService, + createdAt: new Date(), + id: eservice.id, + isSignalHubEnabled, + isDelegable, + isClientAccessDelegable: expectedIsClientAccessDelegable, descriptors: [ { ...mockDescriptor, diff --git a/packages/catalog-process/test/getEserviceById.test.ts b/packages/catalog-process/test/getEserviceById.test.ts index 413fb2573b..a86a37bfd3 100644 --- a/packages/catalog-process/test/getEserviceById.test.ts +++ b/packages/catalog-process/test/getEserviceById.test.ts @@ -35,6 +35,7 @@ describe("get eservice by id", () => { descriptors: [descriptor1], isSignalHubEnabled: true, isDelegable: true, + isClientAccessDelegable: true, }; await addOneEService(eservice1); const authData: AuthData = { diff --git a/packages/catalog-process/test/getEservices.test.ts b/packages/catalog-process/test/getEservices.test.ts index c1c8ae0374..d7cf9dd684 100644 --- a/packages/catalog-process/test/getEservices.test.ts +++ b/packages/catalog-process/test/getEservices.test.ts @@ -62,6 +62,7 @@ describe("get eservices", () => { producerId: organizationId1, isSignalHubEnabled: true, isDelegable: true, + isClientAccessDelegable: true, }; await addOneEService(eservice1); diff --git a/packages/catalog-process/test/updateEservice.test.ts b/packages/catalog-process/test/updateEservice.test.ts index 64b005b1ba..99129cf2da 100644 --- a/packages/catalog-process/test/updateEservice.test.ts +++ b/packages/catalog-process/test/updateEservice.test.ts @@ -16,6 +16,7 @@ import { generateId, } from "pagopa-interop-models"; import { vi, expect, describe, it } from "vitest"; +import { match } from "ts-pattern"; import { eServiceNotFound, eServiceDuplicate, @@ -41,6 +42,11 @@ describe("update eService", () => { const isSignalHubEnabled = randomArrayItem([false, true, undefined]); const isDelegable = randomArrayItem([false, true, undefined]); + const isClientAccessDelegable = match(isDelegable) + .with(undefined, () => undefined) + .with(true, () => randomArrayItem([false, true, undefined])) + .with(false, () => false) + .exhaustive(); const descriptor: Descriptor = { ...getMockDescriptor(), @@ -62,6 +68,7 @@ describe("update eService", () => { mode: "DELIVER", isSignalHubEnabled, isDelegable, + isClientAccessDelegable, }, { authData: getMockAuthData(mockEService.producerId), @@ -76,6 +83,71 @@ describe("update eService", () => { name: updatedName, isSignalHubEnabled, isDelegable, + isClientAccessDelegable, + }; + + const writtenEvent = await readLastEserviceEvent(mockEService.id); + expect(writtenEvent.stream_id).toBe(mockEService.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("DraftEServiceUpdated"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: DraftEServiceUpdatedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + expect(fileManager.delete).not.toHaveBeenCalled(); + }); + + it("should update an eservice correctly handling isClientAccessDelegable when isDelegable is not true", async () => { + vi.spyOn(fileManager, "delete"); + + const isSignalHubEnabled = randomArrayItem([false, true, undefined]); + const isDelegable: false | undefined = randomArrayItem([false, undefined]); + const isClientAccessDelegable = randomArrayItem([false, true, undefined]); + const expectedIsClientAccessDelegable = match(isDelegable) + .with(false, () => false) + .with(undefined, () => undefined) + .exhaustive(); + + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.draft, + interface: mockDocument, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const updatedName = "eservice new name"; + await addOneEService(eservice); + const returnedEService = await catalogService.updateEService( + mockEService.id, + { + name: updatedName, + description: mockEService.description, + technology: "REST", + mode: "DELIVER", + isSignalHubEnabled, + isDelegable, + isClientAccessDelegable, + }, + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const updatedEService: EService = { + ...eservice, + name: updatedName, + isSignalHubEnabled, + isDelegable, + isClientAccessDelegable: expectedIsClientAccessDelegable, }; const writtenEvent = await readLastEserviceEvent(mockEService.id); diff --git a/packages/models/proto/v2/eservice/eservice.proto b/packages/models/proto/v2/eservice/eservice.proto index b8bf626598..5d392de8c5 100644 --- a/packages/models/proto/v2/eservice/eservice.proto +++ b/packages/models/proto/v2/eservice/eservice.proto @@ -14,6 +14,7 @@ message EServiceV2 { EServiceModeV2 mode = 9; optional bool isSignalHubEnabled = 10; optional bool isDelegable = 11; + optional bool isClientAccessDelegable = 12; } diff --git a/packages/models/src/eservice/eservice.ts b/packages/models/src/eservice/eservice.ts index 059349934a..161f85c553 100644 --- a/packages/models/src/eservice/eservice.ts +++ b/packages/models/src/eservice/eservice.ts @@ -107,5 +107,6 @@ export const EService = z.object({ mode: EServiceMode, isSignalHubEnabled: z.boolean().optional(), isDelegable: z.boolean().optional(), + isClientAccessDelegable: z.boolean().optional(), }); export type EService = z.infer;