From 27d56edf88cedd824b5a0fed3d6de9c687a4db4f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 11:34:00 +0200 Subject: [PATCH 001/537] Init purpose scaffold --- packages/purpose-process/.env | 20 + packages/purpose-process/Dockerfile | 41 + .../kubernetes/dev/configmap.yaml | 8 + .../kubernetes/dev/deployment.yaml | 117 ++ .../kubernetes/dev/service.yaml | 14 + .../kubernetes/dev/serviceAccount.yaml | 9 + .../open-api/purpose-service-spec.yml | 1314 +++++++++++++++++ packages/purpose-process/package.json | 51 + packages/purpose-process/tsconfig.json | 7 + packages/purpose-process/vitest.config.ts | 9 + 10 files changed, 1590 insertions(+) create mode 100644 packages/purpose-process/.env create mode 100644 packages/purpose-process/Dockerfile create mode 100644 packages/purpose-process/kubernetes/dev/configmap.yaml create mode 100644 packages/purpose-process/kubernetes/dev/deployment.yaml create mode 100644 packages/purpose-process/kubernetes/dev/service.yaml create mode 100644 packages/purpose-process/kubernetes/dev/serviceAccount.yaml create mode 100644 packages/purpose-process/open-api/purpose-service-spec.yml create mode 100644 packages/purpose-process/package.json create mode 100644 packages/purpose-process/tsconfig.json create mode 100644 packages/purpose-process/vitest.config.ts diff --git a/packages/purpose-process/.env b/packages/purpose-process/.env new file mode 100644 index 0000000000..519a0c9b27 --- /dev/null +++ b/packages/purpose-process/.env @@ -0,0 +1,20 @@ +HOST=0.0.0.0 +PORT=3000 +LOG_LEVEL=info + +EVENTSTORE_DB_HOST=localhost +EVENTSTORE_DB_NAME=root +EVENTSTORE_DB_USERNAME=root +EVENTSTORE_DB_PASSWORD=root +EVENTSTORE_DB_PORT=6001 +EVENTSTORE_DB_SCHEMA=purpose +EVENTSTORE_DB_USE_SSL=false + +READMODEL_DB_HOST=localhost +READMODEL_DB_NAME=readmodel +READMODEL_DB_USERNAME=root +READMODEL_DB_PASSWORD=example +READMODEL_DB_PORT=27017 + +WELL_KNOWN_URLS="https://dev.interop.pagopa.it/.well-known/jwks.json" + diff --git a/packages/purpose-process/Dockerfile b/packages/purpose-process/Dockerfile new file mode 100644 index 0000000000..2caaa23a31 --- /dev/null +++ b/packages/purpose-process/Dockerfile @@ -0,0 +1,41 @@ +FROM node:18.17.1-slim as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ + +COPY ./packages/purpose-process/package.json /app/packages/purpose-process/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/purpose-process /app/packages/purpose-process +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/purpose-process/node_modules \ + package*.json packages/purpose-process/package*.json \ + packages/commons/ \ + packages/models/ \ + packages/purpose-process/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:18.17.1-slim as final + +COPY --from=build /out /app + +WORKDIR /app/packages/purpose-process +EXPOSE 3000 + +CMD [ "node", "." ] diff --git a/packages/purpose-process/kubernetes/dev/configmap.yaml b/packages/purpose-process/kubernetes/dev/configmap.yaml new file mode 100644 index 0000000000..0d1dca875e --- /dev/null +++ b/packages/purpose-process/kubernetes/dev/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: interop-be-purpose-process-refactor + namespace: dev-refactor +data: + EVENTSTORE_DB_USE_SSL: "true" + EVENTSTORE_DB_SCHEMA: "purpose" diff --git a/packages/purpose-process/kubernetes/dev/deployment.yaml b/packages/purpose-process/kubernetes/dev/deployment.yaml new file mode 100644 index 0000000000..1306c221ac --- /dev/null +++ b/packages/purpose-process/kubernetes/dev/deployment.yaml @@ -0,0 +1,117 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: interop-be-purpose-process-refactor + namespace: dev-refactor + labels: + app: interop-be-purpose-process-refactor +spec: + replicas: 1 + selector: + matchLabels: + app: interop-be-purpose-process-refactor + template: + metadata: + labels: + app: interop-be-purpose-process-refactor + spec: + serviceAccountName: interop-be-purpose-process-refactor + containers: + - name: interop-be-purpose-process-refactor + image: ghcr.io/pagopa/purpose-process@$IMAGE_DIGEST + imagePullPolicy: Always + ports: + - name: http + containerPort: 3000 + protocol: TCP + resources: + requests: + cpu: 1.0 + memory: 2Gi + limits: + cpu: 1.0 + memory: 2Gi + livenessProbe: + httpGet: + path: /status + port: http + initialDelaySeconds: 15 + periodSeconds: 5 + readinessProbe: + httpGet: + path: /status + port: http + initialDelaySeconds: 15 + periodSeconds: 5 + env: + - name: PORT + value: "3000" + - name: HOST + value: "0.0.0.0" + - name: LOG_LEVEL + value: info + - name: EVENTSTORE_DB_HOST + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: EVENTSTORE_DB_HOST + - name: EVENTSTORE_DB_NAME + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: EVENTSTORE_DB_NAME + - name: EVENTSTORE_DB_PORT + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: EVENTSTORE_DB_PORT + - name: EVENTSTORE_DB_USERNAME + valueFrom: + secretKeyRef: + name: persistence-management + key: REFACTOR_USERNAME + - name: EVENTSTORE_DB_PASSWORD + valueFrom: + secretKeyRef: + name: persistence-management + key: REFACTOR_USER_PASSWORD + - name: EVENTSTORE_DB_SCHEMA + valueFrom: + configMapKeyRef: + name: interop-be-purpose-process-refactor + key: EVENTSTORE_DB_SCHEMA + - name: EVENTSTORE_DB_USE_SSL + valueFrom: + configMapKeyRef: + name: interop-be-purpose-process-refactor + key: EVENTSTORE_DB_USE_SSL + - name: READMODEL_DB_HOST + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_HOST + - name: READMODEL_DB_NAME + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_NAME + - name: READMODEL_DB_PORT + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_PORT + - name: WELL_KNOWN_URLS + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: WELL_KNOWN_URLS + - name: READMODEL_DB_USERNAME + valueFrom: + secretKeyRef: + name: read-model + key: REFACTOR_USERNAME + - name: READMODEL_DB_PASSWORD + valueFrom: + secretKeyRef: + name: read-model + key: REFACTOR_USER_PASSWORD diff --git a/packages/purpose-process/kubernetes/dev/service.yaml b/packages/purpose-process/kubernetes/dev/service.yaml new file mode 100644 index 0000000000..578ee7118f --- /dev/null +++ b/packages/purpose-process/kubernetes/dev/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: interop-be-purpose-process-refactor + namespace: dev-refactor +spec: + type: ClusterIP + ports: + - port: 3000 + name: http + targetPort: http + protocol: TCP + selector: + app: interop-be-purpose-process-refactor diff --git a/packages/purpose-process/kubernetes/dev/serviceAccount.yaml b/packages/purpose-process/kubernetes/dev/serviceAccount.yaml new file mode 100644 index 0000000000..e78f0a9ec6 --- /dev/null +++ b/packages/purpose-process/kubernetes/dev/serviceAccount.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: interop-be-purpose-process-refactor + namespace: dev-refactor + labels: + app.kubernetes.io/name: interop-be-purpose-process-refactor + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::505630707203:role/interop-be-purpose-process-refactor-dev diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml new file mode 100644 index 0000000000..c54d027e0a --- /dev/null +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -0,0 +1,1314 @@ +openapi: 3.0.3 +info: + title: Purpose Process Micro Service + description: Offers operations to retrieve purpose data + version: '{{version}}' + contact: + name: API Support + url: 'http://www.example.com/support' + email: support@example.com + termsOfService: 'http://swagger.io/terms/' + x-api-id: an x-api-id + x-summary: an x-summary +servers: + - url: '/purpose-process/{{version}}' + description: This service is the purpose process +security: + - bearerAuth: [] +tags: + - name: purpose + description: Implements purpose process + externalDocs: + description: Find out more + url: 'http://swagger.io' + - name: health + description: Verify service status + externalDocs: + description: Find out more + url: 'http://swagger.io' +paths: + /purposes: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + get: + tags: + - purpose + operationId: getPurposes + description: Retrieve Purposes + parameters: + - in: query + name: name + schema: + type: string + - in: query + name: eservicesIds + description: comma separated sequence of EService IDs + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: consumersIds + description: comma separated sequence of consumers IDs + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: producersIds + description: comma separated sequence of producers IDs + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: states + description: comma separated sequence of states + schema: + type: array + items: + $ref: '#/components/schemas/PurposeVersionState' + default: [] + explode: false + - in: query + name: excludeDraft + schema: + type: boolean + default: false + - in: query + name: offset + required: true + schema: + type: integer + format: int32 + minimum: 0 + - in: query + name: limit + required: true + schema: + type: integer + format: int32 + minimum: 1 + maximum: 50 + responses: + '200': + description: Purposes requested + content: + application/json: + schema: + $ref: '#/components/schemas/Purposes' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + post: + tags: + - purpose + operationId: createPurpose + description: Creates the Purpose + responses: + '200': + description: Purpose created + content: + application/json: + schema: + $ref: '#/components/schemas/Purpose' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '403': + description: Forbidden + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '409': + description: Name Conflict + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeSeed' + required: true + /reverse/purposes: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + post: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + tags: + - purpose + operationId: createPurposeFromEService + description: Create a Purpose for EService with Receive mode + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/EServicePurposeSeed' + required: true + responses: + '200': + description: Purpose created + content: + application/json: + schema: + $ref: '#/components/schemas/Purpose' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '403': + description: Forbidden + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '409': + description: Conflict + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /reverse/purposes/{id}: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: id + in: path + description: the purpose id + required: true + schema: + type: string + format: uuid + post: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + tags: + - purpose + operationId: updateReversePurpose + description: Updates a Purpose of an EService + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ReversePurposeUpdateContent' + required: true + responses: + '200': + description: Purpose updated + content: + application/json: + schema: + $ref: '#/components/schemas/Purpose' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /purposes/{id}: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: id + in: path + description: the purpose id + required: true + schema: + type: string + format: uuid + get: + tags: + - purpose + operationId: getPurpose + description: Retrieve the Purpose + responses: + '200': + description: Purpose requested + content: + application/json: + schema: + $ref: '#/components/schemas/Purpose' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + post: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + tags: + - purpose + operationId: updatePurpose + description: Updates a Purpose if not yet activated + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeUpdateContent' + required: true + responses: + '200': + description: Purpose updated + content: + application/json: + schema: + $ref: '#/components/schemas/Purpose' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + delete: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + tags: + - purpose + operationId: deletePurpose + description: Deletes a specific purpose if there are no version or just a draft version + responses: + '204': + description: Purpose deleted + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '403': + description: Purpose has at least one version that is not in draft state or more than one version + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /purposes/{purposeId}/versions: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + description: the purpose id + required: true + schema: + type: string + format: uuid + post: + tags: + - purpose + operationId: createPurposeVersion + description: Creates a draft Purpose Version + responses: + '200': + description: Purpose created + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersion' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersionSeed' + required: true + /purposes/{purposeId}/versions/{versionId}: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + - name: versionId + in: path + required: true + schema: + type: string + format: uuid + delete: + summary: Delete a Purpose Version + operationId: deletePurposeVersion + responses: + '204': + description: Purpose Version Deleted + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + tags: + - purpose + description: deletes the purpose version by id + /purposes/{purposeId}/versions/{versionId}/documents/{documentId}: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + get: + security: + - bearerAuth: [] + tags: + - purpose + summary: Get an Risk Analysis document + operationId: getRiskAnalysisDocument + parameters: + - name: purposeId + in: path + description: the purpose id + required: true + schema: + type: string + - name: versionId + in: path + description: the version Id + required: true + schema: + type: string + - name: documentId + in: path + description: the document id + required: true + schema: + type: string + responses: + '200': + description: Risk Analysis document retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersionDocument' + '404': + description: Resource not found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '400': + description: Bad request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /status: + get: + security: [] + tags: + - health + summary: Health status endpoint + description: Return ok + operationId: getStatus + responses: + '200': + description: successful operation + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /purposes/{purposeId}/versions/{versionId}/activate: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + - name: versionId + in: path + required: true + schema: + type: string + format: uuid + post: + summary: Activate Purpose Version + operationId: activatePurposeVersion + responses: + '200': + description: Purpose Version Activated + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersion' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + tags: + - purpose + description: activates the purpose version by id + /purposes/{purposeId}/clone: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + post: + summary: Clone Purpose + operationId: clonePurpose + responses: + '200': + description: Purpose Cloned + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Purpose' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '409': + description: Purpose cannot be cloned + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + tags: + - purpose + description: clone purpose + /purposes/{purposeId}/versions/{versionId}/suspend: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + - name: versionId + in: path + required: true + schema: + type: string + format: uuid + post: + summary: Suspend Purpose Version + operationId: suspendPurposeVersion + responses: + '200': + description: Purpose Version Suspended + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersion' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + tags: + - purpose + description: suspends the purpose version by id + /purposes/{purposeId}/versions/{versionId}/archive: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + - name: versionId + in: path + required: true + schema: + type: string + format: uuid + post: + summary: Archive Purpose Version + operationId: archivePurposeVersion + responses: + '200': + description: Purpose Version Archived + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersion' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + tags: + - purpose + description: archives the purpose version by id + /purposes/{purposeId}/versions/{versionId}/update/waitingForApproval: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + - name: versionId + in: path + required: true + schema: + type: string + format: uuid + post: + tags: + - purpose + summary: Update a purpose version in waiting for approval + operationId: updateWaitingForApprovalPurposeVersion + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/WaitingForApprovalPurposeVersionUpdateContent' + required: true + responses: + '200': + description: Purpose Version updated + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeVersion' + '400': + description: Invalid input + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '403': + description: Purpose version not in waiting for approval state or the organization is not a producer + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /purposes/riskAnalysis/latest: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: tenantKind + in: query + schema: + $ref: '#/components/schemas/TenantKind' + get: + tags: + - purpose + operationId: retrieveLatestRiskAnalysisConfiguration + description: Retrieve latest risk analysis configuration + responses: + '200': + description: Risk analysis requested + content: + application/json: + schema: + $ref: '#/components/schemas/RiskAnalysisFormConfigResponse' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + /purposes/riskAnalysis/version/{riskAnalysisVersion}: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: riskAnalysisVersion + in: path + required: true + schema: + type: string + - name: tenantKind + in: query + schema: + $ref: '#/components/schemas/TenantKind' + get: + tags: + - purpose + operationId: retrieveRiskAnalysisConfigurationByVersion + description: Retrieve a specified version of risk analysis configuration + responses: + '200': + description: Risk analysis requested + content: + application/json: + schema: + $ref: '#/components/schemas/RiskAnalysisFormConfigResponse' + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Not Found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' +components: + parameters: + CorrelationIdHeader: + in: header + name: X-Correlation-Id + required: true + schema: + type: string + schemas: + TenantKind: + type: string + description: Tenant Kind + enum: + - PA + - PRIVATE + - GSP + RiskAnalysisFormConfigResponse: + type: object + properties: + version: + type: string + questions: + type: array + items: + $ref: '#/components/schemas/FormConfigQuestionResponse' + required: + - version + - questions + FormConfigQuestionResponse: + type: object + properties: + id: + type: string + label: + $ref: '#/components/schemas/LocalizedTextResponse' + infoLabel: + $ref: '#/components/schemas/LocalizedTextResponse' + dataType: + $ref: '#/components/schemas/DataTypeResponse' + required: + type: boolean + dependencies: + type: array + items: + $ref: '#/components/schemas/DependencyResponse' + visualType: + type: string + defaultValue: + type: array + items: + type: string + hideOption: + type: object + additionalProperties: + type: array + items: + $ref: '#/components/schemas/HideOptionResponse' + validation: + $ref: '#/components/schemas/ValidationOptionResponse' + options: + type: array + items: + $ref: '#/components/schemas/LabeledValueResponse' + required: + - id + - label + - dataType + - required + - dependencies + - visualType + - defaultValue + ValidationOptionResponse: + type: object + properties: + maxLength: + type: integer + format: int32 + HideOptionResponse: + type: object + properties: + id: + type: string + value: + type: string + required: + - id + - value + LabeledValueResponse: + type: object + properties: + label: + $ref: '#/components/schemas/LocalizedTextResponse' + value: + type: string + required: + - label + - value + LocalizedTextResponse: + type: object + properties: + it: + type: string + en: + type: string + required: + - it + - en + DataTypeResponse: + type: string + description: Data Type Question + enum: + - SINGLE + - MULTI + - FREETEXT + DependencyResponse: + type: object + properties: + id: + type: string + value: + type: string + required: + - id + - value + Agreement: + type: object + properties: + id: + type: string + format: uuid + state: + $ref: '#/components/schemas/AgreementState' + required: + - id + - state + AgreementState: + type: string + description: Agreement State + enum: + - DRAFT + - ACTIVE + - PENDING + - SUSPENDED + - ARCHIVED + - MISSING_CERTIFIED_ATTRIBUTES + - REJECTED + Client: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + required: + - id + - name + Clients: + type: array + items: + $ref: '#/components/schemas/Client' + EService: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + producer: + $ref: '#/components/schemas/Organization' + descriptor: + $ref: '#/components/schemas/EServiceDescriptor' + required: + - id + - name + - producer + - descriptor + EServiceDescriptor: + type: object + properties: + id: + type: string + format: uuid + version: + type: string + dailyCalls: + type: integer + format: int32 + state: + $ref: '#/components/schemas/EServiceDescriptorState' + required: + - id + - version + - dailyCalls + - state + EServiceDescriptorState: + type: string + description: EService Descriptor State + enum: + - DRAFT + - PUBLISHED + - DEPRECATED + - SUSPENDED + - ARCHIVED + Organization: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + required: + - id + - name + Purpose: + type: object + properties: + id: + type: string + format: uuid + eserviceId: + type: string + format: uuid + consumerId: + type: string + format: uuid + versions: + type: array + items: + $ref: '#/components/schemas/PurposeVersion' + suspendedByConsumer: + type: boolean + suspendedByProducer: + type: boolean + title: + type: string + description: + type: string + riskAnalysisForm: + $ref: '#/components/schemas/RiskAnalysisForm' + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + isRiskAnalysisValid: + type: boolean + isFreeOfCharge: + type: boolean + freeOfChargeReason: + type: string + required: + - id + - eserviceId + - consumerId + - versions + - title + - description + - createdAt + - isRiskAnalysisValid + - isFreeOfCharge + description: business representation of a purpose + Purposes: + type: object + properties: + results: + type: array + items: + $ref: '#/components/schemas/Purpose' + totalCount: + type: integer + format: int32 + required: + - results + - totalCount + PurposeUpdateContent: + type: object + description: contains the expected payload for purpose update. + properties: + title: + type: string + minLength: 5 + maxLength: 60 + description: + type: string + minLength: 10 + maxLength: 250 + isFreeOfCharge: + type: boolean + freeOfChargeReason: + type: string + riskAnalysisForm: + $ref: '#/components/schemas/RiskAnalysisFormSeed' + dailyCalls: + description: 'maximum number of daily calls that this version can perform.' + type: integer + format: int32 + minimum: 0 + required: + - title + - description + - isFreeOfCharge + - dailyCalls + ReversePurposeUpdateContent: + type: object + description: contains the expected payload for purpose update. + properties: + title: + type: string + minLength: 5 + maxLength: 60 + description: + type: string + minLength: 10 + maxLength: 250 + isFreeOfCharge: + type: boolean + freeOfChargeReason: + type: string + dailyCalls: + description: 'maximum number of daily calls that this version can perform.' + type: integer + format: int32 + minimum: 0 + required: + - title + - description + - isFreeOfCharge + - dailyCalls + WaitingForApprovalPurposeVersionUpdateContent: + type: object + description: contains the expected payload for purpose version update. + properties: + expectedApprovalDate: + description: 'Estimated expected approval date for a purpose version' + type: string + format: date-time + required: + - expectedApprovalDate + PurposeVersion: + type: object + properties: + id: + type: string + format: uuid + state: + $ref: '#/components/schemas/PurposeVersionState' + createdAt: + type: string + format: date-time + suspendedAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + firstActivationAt: + type: string + format: date-time + expectedApprovalDate: + type: string + format: date-time + dailyCalls: + description: 'maximum number of daily calls that this version can perform.' + type: integer + format: int32 + minimum: 0 + riskAnalysis: + $ref: '#/components/schemas/PurposeVersionDocument' + required: + - id + - state + - dailyCalls + - createdAt + description: business representation of a purpose version + PurposeVersionState: + type: string + description: Purpose State + enum: + - ACTIVE + - DRAFT + - SUSPENDED + - WAITING_FOR_APPROVAL + - ARCHIVED + PurposeVersionDocument: + type: object + required: + - id + - contentType + - path + - createdAt + properties: + id: + type: string + format: uuid + contentType: + type: string + path: + type: string + createdAt: + type: string + format: date-time + EServicePurposeSeed: + type: object + description: contains the expected payload for purpose creation. + properties: + eServiceId: + type: string + format: uuid + consumerId: + type: string + format: uuid + riskAnalysisId: + type: string + format: uuid + title: + type: string + minLength: 5 + maxLength: 60 + description: + type: string + minLength: 10 + maxLength: 250 + isFreeOfCharge: + type: boolean + freeOfChargeReason: + type: string + dailyCalls: + type: integer + format: int32 + required: + - eServiceId + - consumerId + - title + - riskAnalysisId + - description + - isFreeOfCharge + - dailyCalls + PurposeSeed: + type: object + description: contains the expected payload for purpose creation. + properties: + eserviceId: + type: string + format: uuid + consumerId: + type: string + format: uuid + riskAnalysisForm: + $ref: '#/components/schemas/RiskAnalysisFormSeed' + title: + type: string + minLength: 5 + maxLength: 60 + description: + type: string + minLength: 10 + maxLength: 250 + isFreeOfCharge: + type: boolean + freeOfChargeReason: + type: string + dailyCalls: + type: integer + format: int32 + required: + - eserviceId + - consumerId + - title + - description + - isFreeOfCharge + - dailyCalls + PurposeVersionSeed: + type: object + description: contains the expected payload for purpose version creation. + required: + - dailyCalls + properties: + dailyCalls: + description: 'maximum number of daily calls that this version can perform.' + type: integer + format: int32 + minimum: 0 + RiskAnalysisForm: + type: object + properties: + version: + type: string + minLength: 1 + maxLength: 250 + answers: + additionalProperties: + type: array + items: + type: string + minLength: 1 + maxLength: 250 + riskAnalysisId: + type: string + format: uuid + required: + - version + - answers + RiskAnalysisFormSeed: + type: object + properties: + version: + type: string + minLength: 1 + maxLength: 250 + answers: + additionalProperties: + type: array + items: + type: string + minLength: 1 + maxLength: 250 + required: + - version + - answers + Problem: + properties: + type: + description: URI reference of type definition + type: string + status: + description: The HTTP status code generated by the origin server for this occurrence of the problem. + example: 503 + exclusiveMaximum: true + format: int32 + maximum: 600 + minimum: 100 + type: integer + title: + description: A short, summary of the problem type. Written in english and readable + example: Service Unavailable + maxLength: 64 + pattern: '^[ -~]{0,64}$' + type: string + correlationId: + description: Unique identifier of the request + example: '53af4f2d-0c87-41ef-a645-b726a821852b' + maxLength: 64 + type: string + detail: + description: A human readable explanation of the problem. + example: Request took too long to complete. + maxLength: 4096 + pattern: '^.{0,1024}$' + type: string + errors: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/ProblemError' + additionalProperties: false + required: + - type + - status + - title + - errors + ProblemError: + properties: + code: + description: Internal code of the error + example: 123-4567 + minLength: 8 + maxLength: 8 + pattern: '^[0-9]{3}-[0-9]{4}$' + type: string + detail: + description: A human readable explanation specific to this occurrence of the problem. + example: Parameter not valid + maxLength: 4096 + pattern: '^.{0,1024}$' + type: string + required: + - code + - detail + securitySchemes: + bearerAuth: + type: http + description: 'A bearer token in the format of a JWS and conformed to the specifications included in [RFC8725](https://tools.ietf.org/html/RFC8725).' + scheme: bearer + bearerFormat: JWT diff --git a/packages/purpose-process/package.json b/packages/purpose-process/package.json new file mode 100644 index 0000000000..10df6537c3 --- /dev/null +++ b/packages/purpose-process/package.json @@ -0,0 +1,51 @@ +{ + "name": "pagopa-interop-purpose-process", + "version": "1.0.0", + "description": "PagoPA Interoperability service for purpose", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "node --watch --no-warnings --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", + "build": "tsc", + "generate-model": "mkdir -p ./src/model/generated && pnpm openapi-zod-client './open-api/purpose-service-spec.yml' -o './src/model/generated/api.ts'", + "clean-generated": "pnpm exec rm ./src/model/generated/api.ts" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@protobuf-ts/runtime": "2.9.1", + "@types/axios": "0.14.0", + "@types/dotenv-flow": "3.2.0", + "@types/express": "4.17.17", + "@types/node": "20.3.1", + "@types/uuid": "9.0.2", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "testcontainers": "10.2.2", + "ts-node": "10.9.2", + "typescript": "5.1.3", + "vitest": "0.33.0" + }, + "dependencies": { + "@zodios/core": "10.9.2", + "@zodios/express": "10.6.1", + "axios": "1.4.0", + "dotenv-flow": "3.2.0", + "express": "4.18.2", + "mongodb": "5.6.0", + "openapi-zod-client": "1.15.1", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "pg-promise": "11.5.0", + "ts-pattern": "5.0.6", + "uuid": "9.0.0", + "zod": "3.21.4" + } +} diff --git a/packages/purpose-process/tsconfig.json b/packages/purpose-process/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/purpose-process/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/purpose-process/vitest.config.ts b/packages/purpose-process/vitest.config.ts new file mode 100644 index 0000000000..498a780952 --- /dev/null +++ b/packages/purpose-process/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + setupFiles: ["dotenv/config"], + testTimeout: 60000, + hookTimeout: 60000, + }, +}); From 5ade8e9c9c7a60e3789a192cda1ee134d73f748e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 11:52:34 +0200 Subject: [PATCH 002/537] Scaffold wip --- packages/purpose-process/src/app.ts | 26 +++++ packages/purpose-process/src/index.ts | 7 ++ .../src/routers/HealthRouter.ts | 8 ++ .../src/routers/PurposeRouter.ts | 20 ++++ .../purpose-process/src/utilities/config.ts | 15 +++ pnpm-lock.yaml | 107 ++++++++++++++---- 6 files changed, 161 insertions(+), 22 deletions(-) create mode 100644 packages/purpose-process/src/app.ts create mode 100644 packages/purpose-process/src/index.ts create mode 100644 packages/purpose-process/src/routers/HealthRouter.ts create mode 100644 packages/purpose-process/src/routers/PurposeRouter.ts create mode 100644 packages/purpose-process/src/utilities/config.ts diff --git a/packages/purpose-process/src/app.ts b/packages/purpose-process/src/app.ts new file mode 100644 index 0000000000..f3269ea050 --- /dev/null +++ b/packages/purpose-process/src/app.ts @@ -0,0 +1,26 @@ +import { + contextDataMiddleware, + globalContextMiddleware, + loggerMiddleware, + authenticationMiddleware, + zodiosCtx, +} from "pagopa-interop-commons"; +import purposeRouter from "./routers/PurposeRouter.js"; +import healthRouter from "./routers/HealthRouter.js"; + +const app = zodiosCtx.app(); + +// Disable the "X-Powered-By: Express" HTTP header for security reasons. +// See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 +app.disable("x-powered-by"); + +app.use(globalContextMiddleware); +app.use(contextDataMiddleware); +app.use(loggerMiddleware("purpose-process")()); +// NOTE(gabro): the order is relevant, authMiddleware must come *after* the routes +// we want to be unauthenticated. +app.use(healthRouter); +app.use(authenticationMiddleware()); +app.use(purposeRouter(zodiosCtx)); + +export default app; diff --git a/packages/purpose-process/src/index.ts b/packages/purpose-process/src/index.ts new file mode 100644 index 0000000000..4d0dbf1775 --- /dev/null +++ b/packages/purpose-process/src/index.ts @@ -0,0 +1,7 @@ +import { logger } from "pagopa-interop-commons"; +import { config } from "./utilities/config.js"; +import app from "./app.js"; + +app.listen(config.port, config.host, () => { + logger.info(`listening on ${config.host}:${config.port}`); +}); diff --git a/packages/purpose-process/src/routers/HealthRouter.ts b/packages/purpose-process/src/routers/HealthRouter.ts new file mode 100644 index 0000000000..c8d635d5de --- /dev/null +++ b/packages/purpose-process/src/routers/HealthRouter.ts @@ -0,0 +1,8 @@ +import { zodiosRouter } from "@zodios/express"; +import { api } from "../model/generated/api.js"; + +const healthRouter = zodiosRouter(api.api); + +healthRouter.get("/status", async (_, res) => res.status(200).end()); + +export default healthRouter; diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts new file mode 100644 index 0000000000..e410146247 --- /dev/null +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -0,0 +1,20 @@ +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { + ExpressContext, + userRoles, + ZodiosContext, + authorizationMiddleware, +} from "pagopa-interop-commons"; +import { api } from "../model/generated/api.js"; + +const purposeRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const purposeRouter = ctx.router(api.api); + const { ADMIN_ROLE } = userRoles; + purposeRouter.get("/purposes", authorizationMiddleware([ADMIN_ROLE])); + + return purposeRouter; +}; +export default purposeRouter; diff --git a/packages/purpose-process/src/utilities/config.ts b/packages/purpose-process/src/utilities/config.ts new file mode 100644 index 0000000000..afd7b2eeda --- /dev/null +++ b/packages/purpose-process/src/utilities/config.ts @@ -0,0 +1,15 @@ +import { + CommonConfig, + ReadModelDbConfig, + EventStoreConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const PurposeProcessConfig = + CommonConfig.and(ReadModelDbConfig).and(EventStoreConfig); + +export type PurposeProcessConfig = z.infer; + +export const config: PurposeProcessConfig = { + ...PurposeProcessConfig.parse(process.env), +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index edc1f3c42f..2e834c2ec1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -873,6 +873,88 @@ importers: specifier: 0.33.0 version: 0.33.0 + packages/purpose-process: + dependencies: + '@zodios/core': + specifier: 10.9.2 + version: 10.9.2(axios@1.4.0)(zod@3.21.4) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.2)(express@4.18.2)(zod@3.21.4) + axios: + specifier: 1.4.0 + version: 1.4.0 + dotenv-flow: + specifier: 3.2.0 + version: 3.2.0 + express: + specifier: 4.18.2 + version: 4.18.2 + mongodb: + specifier: 5.6.0 + version: 5.6.0 + openapi-zod-client: + specifier: 1.15.1 + version: 1.15.1 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + pg-promise: + specifier: 11.5.0 + version: 11.5.0 + ts-pattern: + specifier: 5.0.6 + version: 5.0.6 + uuid: + specifier: 9.0.0 + version: 9.0.0 + zod: + specifier: 3.21.4 + version: 3.21.4 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(typescript@5.1.3) + '@protobuf-ts/runtime': + specifier: 2.9.1 + version: 2.9.1 + '@types/axios': + specifier: 0.14.0 + version: 0.14.0 + '@types/dotenv-flow': + specifier: 3.2.0 + version: 3.2.0 + '@types/express': + specifier: 4.17.17 + version: 4.17.17 + '@types/node': + specifier: 20.3.1 + version: 20.3.1 + '@types/uuid': + specifier: 9.0.2 + version: 9.0.2 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.2.2 + version: 10.2.2 + ts-node: + specifier: 10.9.2 + version: 10.9.2(@types/node@20.3.1)(typescript@5.1.3) + typescript: + specifier: 5.1.3 + version: 5.1.3 + vitest: + specifier: 0.33.0 + version: 0.33.0 + packages/tenant-process: dependencies: '@types/uuid': @@ -5034,10 +5116,6 @@ packages: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} dev: true - /async-lock@1.4.0: - resolution: {integrity: sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==} - dev: true - /async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} dev: true @@ -5565,13 +5643,6 @@ packages: path-type: 4.0.0 dev: true - /docker-compose@0.24.3: - resolution: {integrity: sha512-x3/QN3AIOMe7j2c8f/jcycizMft7dl8MluoB9OGPAYCyKHHiPUFqI9GjCcsU0kYy24vYKMCcfR6+5ZaEyQlrxg==} - engines: {node: '>= 6.0.0'} - dependencies: - yaml: 2.3.1 - dev: true - /docker-compose@0.24.7: resolution: {integrity: sha512-CdHl9n0S4+bl4i6MaxDQHNjqB1FdvuDirrDTzPKmdiMpheQqCjgsny0GZ2VhvN7qHTY0833lRlKWZgrkn1i6cg==} engines: {node: '>= 6.0.0'} @@ -8119,14 +8190,6 @@ packages: tar-stream: 2.2.0 dev: true - /tar-fs@3.0.4: - resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} - dependencies: - mkdirp-classic: 0.5.3 - pump: 3.0.0 - tar-stream: 3.1.6 - dev: true - /tar-fs@3.0.5: resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} dependencies: @@ -8161,17 +8224,17 @@ packages: dependencies: '@balena/dockerignore': 1.0.2 archiver: 5.3.2 - async-lock: 1.4.0 + async-lock: 1.4.1 byline: 5.0.0 debug: 4.3.4 - docker-compose: 0.24.3 + docker-compose: 0.24.7 dockerode: 3.3.5 get-port: 5.1.1 node-fetch: 2.7.0 proper-lockfile: 4.1.2 properties-reader: 2.3.0 ssh-remote-port-forward: 1.0.4 - tar-fs: 3.0.4 + tar-fs: 3.0.5 tmp: 0.2.1 transitivePeerDependencies: - encoding From e7b2f63f0d7e731691a6e072f6f3178c496db419 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 11:55:41 +0200 Subject: [PATCH 003/537] Remove comment --- packages/purpose-process/src/app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/purpose-process/src/app.ts b/packages/purpose-process/src/app.ts index f3269ea050..58ca9b8177 100644 --- a/packages/purpose-process/src/app.ts +++ b/packages/purpose-process/src/app.ts @@ -17,8 +17,6 @@ app.disable("x-powered-by"); app.use(globalContextMiddleware); app.use(contextDataMiddleware); app.use(loggerMiddleware("purpose-process")()); -// NOTE(gabro): the order is relevant, authMiddleware must come *after* the routes -// we want to be unauthenticated. app.use(healthRouter); app.use(authenticationMiddleware()); app.use(purposeRouter(zodiosCtx)); From db3837e83320d64ef78ccdb4728b895775a9cbfb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 12:25:44 +0200 Subject: [PATCH 004/537] Continue scaffold --- .../src/repositories/ReadModelRepository.ts | 5 +- packages/models/src/brandedIds.ts | 16 +++- packages/models/src/index.ts | 2 + packages/models/src/purpose/purpose.ts | 62 ++++++++++++++++ packages/purpose-process/test/.eslintrc.json | 6 ++ .../test/purposeService.integration.test.ts | 73 +++++++++++++++++++ packages/purpose-process/test/tsconfig.json | 4 + pnpm-lock.yaml | 4 + 8 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 packages/models/src/purpose/purpose.ts create mode 100644 packages/purpose-process/test/.eslintrc.json create mode 100644 packages/purpose-process/test/purposeService.integration.test.ts create mode 100644 packages/purpose-process/test/tsconfig.json diff --git a/packages/commons/src/repositories/ReadModelRepository.ts b/packages/commons/src/repositories/ReadModelRepository.ts index a7eff43051..0c8c8ab04f 100644 --- a/packages/commons/src/repositories/ReadModelRepository.ts +++ b/packages/commons/src/repositories/ReadModelRepository.ts @@ -2,6 +2,7 @@ import { Agreement, AttributeReadmodel, EServiceReadModel, + Purpose, Tenant, genericError, } from "pagopa-interop-models"; @@ -32,12 +33,14 @@ export type EServiceCollection = GenericCollection; export type AgreementCollection = GenericCollection; export type TenantCollection = GenericCollection; export type AttributeCollection = GenericCollection; +export type PurposeCollection = GenericCollection; export type Collections = | EServiceCollection | AgreementCollection | TenantCollection - | AttributeCollection; + | AttributeCollection + | PurposeCollection; type BuildQueryKey = `${TPrefix}.${TKey & string}`; diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 8c3caa87f0..462714a1a7 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -47,6 +47,18 @@ export type RiskAnalysisFormId = z.infer; export const RiskAnalysisId = z.string().uuid().brand("RiskAnalysisId"); export type RiskAnalysisId = z.infer; +export const PurposeId = z.string().uuid().brand("PurposeId"); +export type PurposeId = z.infer; + +export const PurposeVersionId = z.string().uuid().brand("PurposeVersionId"); +export type PurposeVersionId = z.infer; + +export const PurposeVersionDocumentId = z + .string() + .uuid() + .brand("PurposeVersionDocumentId"); +export type PurposeVersionDocumentId = z.infer; + type IDS = | EServiceId | EServiceDocumentId @@ -58,7 +70,9 @@ type IDS = | RiskAnalysisSingleAnswerId | RiskAnalysisMultiAnswerId | RiskAnalysisFormId - | RiskAnalysisId; + | RiskAnalysisId + | PurposeId + | PurposeVersionDocumentId; // This function is used to generate a new ID for a new object // it infers the type of the ID based on how is used the result diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index de35020cd4..1016f7878c 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -29,6 +29,8 @@ export * from "./tenant/protobufConverter.js"; export * from "./tenant/tenant.js"; export * from "./tenant/tenantEvents.js"; +export * from "./purpose/purpose.js"; + export * from "./user/user.js"; // Protobuf diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts new file mode 100644 index 0000000000..c04b6365eb --- /dev/null +++ b/packages/models/src/purpose/purpose.ts @@ -0,0 +1,62 @@ +import { z } from "zod"; +import { + EServiceId, + PurposeId, + PurposeVersionDocumentId, + PurposeVersionId, + TenantId, +} from "../brandedIds.js"; +import { RiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; + +export const purposeVersionState = { + draft: "Draft", + active: "Active", + suspended: "Suspended", + archived: "Archived", + waitingForApproval: "WaitingForApproval", + rejected: "Rejected", +} as const; +export const PurposeVersionState = z.enum([ + Object.values(purposeVersionState)[0], + ...Object.values(purposeVersionState).slice(1), +]); +export type PurposeVersionState = z.infer; + +export const PurposeVersionDocument = z.object({ + id: PurposeVersionDocumentId, + contentType: z.string(), + path: z.string(), + createdAt: z.coerce.date(), +}); +export type PurposeVersionDocument = z.infer; + +export const PurposeVersion = z.object({ + id: PurposeVersionId, + state: PurposeVersionState, + expectedApprovalDate: z.coerce.date().optional(), + riskAnalysys: PurposeVersionDocument.optional(), + dailyCalls: z.number(), + rejectionReason: z.string().optional(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date().optional(), + firstActivationAt: z.coerce.date().optional(), + suspendedAt: z.coerce.date().optional(), +}); +export type PurposeVersion = z.infer; + +export const Purpose = z.object({ + id: PurposeId, + eserviceId: EServiceId, + consumerId: TenantId, + versions: z.array(PurposeVersion), + suspendedByCondumer: z.boolean().optional(), + suspendedByProducer: z.boolean().optional(), + title: z.string(), + description: z.string(), + riskAnalysisForm: RiskAnalysisForm.optional(), + createdAt: z.coerce.date(), + updatedAt: z.coerce.date(), + isFreeOfCharge: z.boolean(), + freeOfChargeReason: z.string().optional(), +}); +export type Purpose = z.infer; diff --git a/packages/purpose-process/test/.eslintrc.json b/packages/purpose-process/test/.eslintrc.json new file mode 100644 index 0000000000..887285214c --- /dev/null +++ b/packages/purpose-process/test/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../../.eslintrc.cjs"], + "rules": { + "functional/immutable-data": "off" + } +} diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts new file mode 100644 index 0000000000..862cf931d1 --- /dev/null +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -0,0 +1,73 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable functional/no-let */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ + +import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { + PurposeCollection, + ReadModelRepository, + initDB, +} from "pagopa-interop-commons"; +import { IDatabase } from "pg-promise"; + +import { + TEST_MONGO_DB_PORT, + TEST_POSTGRES_DB_PORT, + mongoDBContainer, + postgreSQLContainer, +} from "pagopa-interop-commons-test"; +import { StartedTestContainer } from "testcontainers"; +import { config } from "../src/utilities/config.js"; + +describe("database test", async () => { + let purposes: PurposeCollection; + // let readModelService: ReadModelService; + // let catalogService: CatalogService; + let postgresDB: IDatabase; + let startedPostgreSqlContainer: StartedTestContainer; + let startedMongodbContainer: StartedTestContainer; + + beforeAll(async () => { + startedPostgreSqlContainer = await postgreSQLContainer(config).start(); + startedMongodbContainer = await mongoDBContainer(config).start(); + + config.eventStoreDbPort = startedPostgreSqlContainer.getMappedPort( + TEST_POSTGRES_DB_PORT + ); + config.readModelDbPort = + startedMongodbContainer.getMappedPort(TEST_MONGO_DB_PORT); + + const readModelRepository = ReadModelRepository.init(config); + // readModelService = readModelServiceBuilder(readModelRepository); + postgresDB = initDB({ + username: config.eventStoreDbUsername, + password: config.eventStoreDbPassword, + host: config.eventStoreDbHost, + port: config.eventStoreDbPort, + database: config.eventStoreDbName, + schema: config.eventStoreDbSchema, + useSSL: config.eventStoreDbUseSSL, + }); + // purposeService = purposeServiceBuilder(postgresDB, readModelService); + }); + + afterEach(async () => { + await purposes.deleteMany({}); + + await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); + }); + + afterAll(async () => { + await startedPostgreSqlContainer.stop(); + await startedMongodbContainer.stop(); + }); + + describe("Purpose service", () => { + describe("create purpose", () => { + it("TO DO", () => { + expect(1).toBe(1); + }); + }); + }); +}); diff --git a/packages/purpose-process/test/tsconfig.json b/packages/purpose-process/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/purpose-process/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0bf85fe46..fedd0d80de 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3728,6 +3728,10 @@ packages: '@protobuf-ts/runtime': 2.9.4 dev: false + /@protobuf-ts/runtime@2.9.1: + resolution: {integrity: sha512-ZTc8b+pQ6bwxZa3qg9/IO/M/brRkvr0tic9cSGgAsDByfPrtatT2300wTIRLDk8X9WTW1tT+FhyqmcrbMHTeww==} + dev: true + /@protobuf-ts/runtime@2.9.4: resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} From 0962f3182d06d5a95a97770c55540e3946326afb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 12:27:46 +0200 Subject: [PATCH 005/537] Add table for purpose events --- docker/event-store-init.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docker/event-store-init.sql b/docker/event-store-init.sql index c756e8aa22..0f90db6776 100644 --- a/docker/event-store-init.sql +++ b/docker/event-store-init.sql @@ -73,3 +73,22 @@ create table attribute.events ( PRIMARY KEY (sequence_num), UNIQUE (stream_id, version) ); + +create schema purpose; +create table purpose.events ( + sequence_num bigserial NOT NULL, + + stream_id uuid NOT NULL, + version bigint NOT NULL, + + correlation_id text, + + type text NOT NULL, + event_version int NOT NULL, + data bytea NOT NULL, + + log_date timestamptz NOT NULL DEFAULT now(), + + PRIMARY KEY (sequence_num), + UNIQUE (stream_id, version) +); From c381942bd3c53ecc2ef8649597d0cff793cec51b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 12:34:51 +0200 Subject: [PATCH 006/537] Fix ReadModelRepository --- packages/commons/src/repositories/ReadModelRepository.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/commons/src/repositories/ReadModelRepository.ts b/packages/commons/src/repositories/ReadModelRepository.ts index 0c8c8ab04f..567d11f896 100644 --- a/packages/commons/src/repositories/ReadModelRepository.ts +++ b/packages/commons/src/repositories/ReadModelRepository.ts @@ -138,6 +138,8 @@ export class ReadModelRepository { public attributes: AttributeCollection; + public purposes: PurposeCollection; + private client: MongoClient; private db: Db; @@ -161,6 +163,7 @@ export class ReadModelRepository { this.attributes = this.db.collection("attributes", { ignoreUndefined: true, }); + this.purposes = this.db.collection("purpose", { ignoreUndefined: true }); } public static init(config: ReadModelDbConfig): ReadModelRepository { From 0aafdb575bb132c028962ea5773c604054bd579b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 12:34:56 +0200 Subject: [PATCH 007/537] Fix test --- packages/purpose-process/test/purposeService.integration.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 862cf931d1..a70864d046 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -39,6 +39,7 @@ describe("database test", async () => { startedMongodbContainer.getMappedPort(TEST_MONGO_DB_PORT); const readModelRepository = ReadModelRepository.init(config); + purposes = readModelRepository.purposes; // readModelService = readModelServiceBuilder(readModelRepository); postgresDB = initDB({ username: config.eventStoreDbUsername, From 88766ed26a378e2234da6e40c6b3a8807431a4d6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 13:41:06 +0200 Subject: [PATCH 008/537] Adjust placeholder --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a70864d046..6baca7fcbf 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -23,7 +23,7 @@ import { config } from "../src/utilities/config.js"; describe("database test", async () => { let purposes: PurposeCollection; // let readModelService: ReadModelService; - // let catalogService: CatalogService; + // let purposeService: PurposeService; let postgresDB: IDatabase; let startedPostgreSqlContainer: StartedTestContainer; let startedMongodbContainer: StartedTestContainer; From 0289848f7a97db35b6befde960c8a7d0254f1a19 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 13:41:15 +0200 Subject: [PATCH 009/537] Add endpoints scaffold --- .../src/routers/PurposeRouter.ts | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index e410146247..b96ea5cec7 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -13,7 +13,84 @@ const purposeRouter = ( ): ZodiosRouter => { const purposeRouter = ctx.router(api.api); const { ADMIN_ROLE } = userRoles; - purposeRouter.get("/purposes", authorizationMiddleware([ADMIN_ROLE])); + purposeRouter + .get("/purposes", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => + res.status(501).send() + ) + .post("/purposes", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => + res.status(501).send() + ) + .post( + "/reverse/purposes", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/reverse/purposes/:id", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .get("/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => + res.status(501).send() + ) + .post("/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => + res.status(501).send() + ) + .delete( + "/purposes/:id", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/purposes/:purposeId/versions", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .delete( + "/purposes/:purposeId/versions/:versionId", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .get( + "/purposes/:purposeId/versions/:versionId/documents/:documentId", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/purposes/:purposeId/versions/:versionId/activate", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/purposes/:purposeId/clone", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/purposes/:purposeId/versions/:versionId/suspend", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/purposes/:purposeId/versions/:versionId/archive", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .post( + "/purposes/:purposeId/versions/:versionId/update/waitingForApproval", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .get( + "/purposes/riskAnalysis/latest", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) + .get( + "/purposes/riskAnalysis/version/:riskAnalysisVersion", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ); return purposeRouter; }; From fcd5e410555b5a64475728df1b1ed0c8df2d248b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 13:44:55 +0200 Subject: [PATCH 010/537] Remove unused imports --- packages/purpose-process/package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/purpose-process/package.json b/packages/purpose-process/package.json index 10df6537c3..3ee848d0d2 100644 --- a/packages/purpose-process/package.json +++ b/packages/purpose-process/package.json @@ -20,12 +20,9 @@ "license": "Apache-2.0", "devDependencies": { "@pagopa/eslint-config": "3.0.0", - "@protobuf-ts/runtime": "2.9.1", - "@types/axios": "0.14.0", "@types/dotenv-flow": "3.2.0", "@types/express": "4.17.17", "@types/node": "20.3.1", - "@types/uuid": "9.0.2", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.2.2", @@ -36,7 +33,6 @@ "dependencies": { "@zodios/core": "10.9.2", "@zodios/express": "10.6.1", - "axios": "1.4.0", "dotenv-flow": "3.2.0", "express": "4.18.2", "mongodb": "5.6.0", @@ -45,7 +41,6 @@ "pagopa-interop-models": "workspace:*", "pg-promise": "11.5.0", "ts-pattern": "5.0.6", - "uuid": "9.0.0", "zod": "3.21.4" } } From 2a733e17a6786b109ae0b8931e311e1afa071be7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 13:46:20 +0200 Subject: [PATCH 011/537] Update pnpm-lock --- pnpm-lock.yaml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fedd0d80de..2d3d6641f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -881,9 +881,6 @@ importers: '@zodios/express': specifier: 10.6.1 version: 10.6.1(@zodios/core@10.9.2)(express@4.18.2)(zod@3.21.4) - axios: - specifier: 1.4.0 - version: 1.4.0 dotenv-flow: specifier: 3.2.0 version: 3.2.0 @@ -908,9 +905,6 @@ importers: ts-pattern: specifier: 5.0.6 version: 5.0.6 - uuid: - specifier: 9.0.0 - version: 9.0.0 zod: specifier: 3.21.4 version: 3.21.4 @@ -918,12 +912,6 @@ importers: '@pagopa/eslint-config': specifier: 3.0.0 version: 3.0.0(typescript@5.1.3) - '@protobuf-ts/runtime': - specifier: 2.9.1 - version: 2.9.1 - '@types/axios': - specifier: 0.14.0 - version: 0.14.0 '@types/dotenv-flow': specifier: 3.2.0 version: 3.2.0 @@ -933,9 +921,6 @@ importers: '@types/node': specifier: 20.3.1 version: 20.3.1 - '@types/uuid': - specifier: 9.0.2 - version: 9.0.2 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -3728,10 +3713,6 @@ packages: '@protobuf-ts/runtime': 2.9.4 dev: false - /@protobuf-ts/runtime@2.9.1: - resolution: {integrity: sha512-ZTc8b+pQ6bwxZa3qg9/IO/M/brRkvr0tic9cSGgAsDByfPrtatT2300wTIRLDk8X9WTW1tT+FhyqmcrbMHTeww==} - dev: true - /@protobuf-ts/runtime@2.9.4: resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} From 2865274209ab90cf48ef2356a5b29fca23fdb53a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 13:50:08 +0200 Subject: [PATCH 012/537] Add start script --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c833ab9a00..1f1947b8f1 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "start:tenant-readmodel-writer": "turbo start --filter pagopa-interop-tenant-readmodel-writer", "start:auth-updater": "turbo start --filter pagopa-interop-authorization-updater", "start:notifier-seeder": "turbo start --filter pagopa-interop-notifier-seeder", + "start:purpose": "turbo start --filter pagopa-interop-purpose-process", "test": "turbo test", "build": "turbo build", "lint": "turbo lint", @@ -25,8 +26,8 @@ "infra:start": "./scripts/infra-start.sh", "infra:stop": "./scripts/infra-stop.sh", "infra:destroy": "./scripts/infra-destroy.sh", - "prune-dist" : "find . -name 'dist' -type d -exec rm -rf {} +", - "prune-modules" : "find . -name 'node_modules' -type d -exec rm -rf {} +" + "prune-dist": "find . -name 'dist' -type d -exec rm -rf {} +", + "prune-modules": "find . -name 'node_modules' -type d -exec rm -rf {} +" }, "devDependencies": { "@tsconfig/node-lts": "18.12.5", From 97db8092ad5a3d16495ebb8a73c67963b06329f0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 15:44:57 +0200 Subject: [PATCH 013/537] Add protobuf definition --- packages/models/proto/v1/purpose/events.proto | 53 +++++++++++++++++++ .../models/proto/v1/purpose/purpose.proto | 50 +++++++++++++++++ .../proto/v1/purpose/riskAnalysis.proto | 23 ++++++++ 3 files changed, 126 insertions(+) create mode 100644 packages/models/proto/v1/purpose/events.proto create mode 100644 packages/models/proto/v1/purpose/purpose.proto create mode 100644 packages/models/proto/v1/purpose/riskAnalysis.proto diff --git a/packages/models/proto/v1/purpose/events.proto b/packages/models/proto/v1/purpose/events.proto new file mode 100644 index 0000000000..11bd8d2961 --- /dev/null +++ b/packages/models/proto/v1/purpose/events.proto @@ -0,0 +1,53 @@ +syntax = "proto2"; + +package purpose; + +import "v1/purpose/purpose.proto"; + +message PurposeCreatedV1 { + required PurposeV1 purpose = 1; +} + +message PurposeVersionCreatedV1 { + required string purposeId = 1; + required PurposeVersionV1 version = 2; +} + +message PurposeUpdatedV1 { + required PurposeV1 purpose = 1; +} + +message PurposeVersionUpdatedV1 { + required string purposeId = 1; + required PurposeVersionV1 version = 2; +} + +message PurposeVersionActivatedV1 { + required PurposeV1 purpose = 1; +} + +message PurposeVersionRejectedV1 { + required PurposeV1 purpose = 1; + required string versionId = 2; +} + +message PurposeVersionSuspendedV1 { + required PurposeV1 purpose = 1; +} + +message PurposeVersionArchivedV1 { + required PurposeV1 purpose = 1; +} + +message PurposeVersionWaitedForApprovalV1 { + required PurposeV1 purpose = 1; +} + +message PurposeDeletedV1 { + required string purposeId = 1; +} + +message PurposeVersionDeletedV1 { + required string purposeId = 1; + required string versionId = 2; +} \ No newline at end of file diff --git a/packages/models/proto/v1/purpose/purpose.proto b/packages/models/proto/v1/purpose/purpose.proto new file mode 100644 index 0000000000..073b34f307 --- /dev/null +++ b/packages/models/proto/v1/purpose/purpose.proto @@ -0,0 +1,50 @@ +syntax = "proto2"; + +package purpose; + +import "v1/purpose/riskAnalysis.proto"; + +message PurposeV1 { + required string id = 1; + required string eserviceId = 2; + required string consumerId = 3; + optional bool suspendedByConsumer = 5; + optional bool suspendedByProducer = 6; + repeated PurposeVersionV1 versions = 7; + required string title = 8; + required string description = 9; + optional RiskAnalysisFormV1 riskAnalysisForm = 10; + required int64 createdAt = 11; + optional int64 updatedAt = 12; + optional bool isFreeOfCharge = 13; + optional string freeOfChargeReason = 14; +} + +message PurposeVersionV1 { + required string id = 1; + required PurposeStateV1 state = 2; + optional PurposeVersionDocumentV1 riskAnalysis = 3; + required int32 dailyCalls = 4; + required int64 createdAt = 5; + optional int64 updatedAt = 6; + optional int64 firstActivationAt = 7; + optional int64 expectedApprovalDate = 8; + optional int64 suspendedAt = 9; + optional string rejectionReason = 10; +} + +enum PurposeStateV1 { + DRAFT = 1; + ACTIVE = 2; + SUSPENDED = 3; + WAITING_FOR_APPROVAL = 4; + ARCHIVED = 5; + REJECTED = 6; +} + +message PurposeVersionDocumentV1 { + required string id = 1; + required string contentType = 2; + required string path = 3; + required int64 createdAt = 4; +} \ No newline at end of file diff --git a/packages/models/proto/v1/purpose/riskAnalysis.proto b/packages/models/proto/v1/purpose/riskAnalysis.proto new file mode 100644 index 0000000000..ed1ad5f296 --- /dev/null +++ b/packages/models/proto/v1/purpose/riskAnalysis.proto @@ -0,0 +1,23 @@ +syntax = "proto2"; + +package purpose; + +message RiskAnalysisFormV1 { + required string id = 1; + required string version = 2; + repeated RiskAnalysisSingleAnswerV1 singleAnswers = 3; + repeated RiskAnalysisMultiAnswerV1 multiAnswers = 4; + optional string riskAnalysisId = 5; +} + +message RiskAnalysisSingleAnswerV1 { + required string id = 1; + required string key = 2; + optional string value = 3; +} + +message RiskAnalysisMultiAnswerV1 { + required string id = 1; + required string key = 2; + repeated string values = 3; +} \ No newline at end of file From 8fd4b1bf38224dd4d781e0fb3316b80556ce3485 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 16:33:20 +0200 Subject: [PATCH 014/537] Improve model --- packages/models/src/brandedIds.ts | 11 ++++++++++- packages/models/src/purpose/purpose.ts | 4 ++-- packages/models/src/risk-analysis/riskAnalysis.ts | 10 ++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 462714a1a7..b43b92657e 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -44,6 +44,14 @@ export type RiskAnalysisMultiAnswerId = z.infer< export const RiskAnalysisFormId = z.string().uuid().brand("RiskAnalysisFormId"); export type RiskAnalysisFormId = z.infer; +export const PurposeRiskAnalysisFormId = z + .string() + .uuid() + .brand("PurposeRiskAnalysisFormId"); +export type PurposeRiskAnalysisFormId = z.infer< + typeof PurposeRiskAnalysisFormId +>; + export const RiskAnalysisId = z.string().uuid().brand("RiskAnalysisId"); export type RiskAnalysisId = z.infer; @@ -72,7 +80,8 @@ type IDS = | RiskAnalysisFormId | RiskAnalysisId | PurposeId - | PurposeVersionDocumentId; + | PurposeVersionDocumentId + | PurposeRiskAnalysisFormId; // This function is used to generate a new ID for a new object // it infers the type of the ID based on how is used the result diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index c04b6365eb..9b8ae606a8 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -6,7 +6,7 @@ import { PurposeVersionId, TenantId, } from "../brandedIds.js"; -import { RiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; +import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; export const purposeVersionState = { draft: "Draft", @@ -53,7 +53,7 @@ export const Purpose = z.object({ suspendedByProducer: z.boolean().optional(), title: z.string(), description: z.string(), - riskAnalysisForm: RiskAnalysisForm.optional(), + riskAnalysisForm: PurposeRiskAnalysisForm.optional(), createdAt: z.coerce.date(), updatedAt: z.coerce.date(), isFreeOfCharge: z.boolean(), diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index ad0bf4a893..1f79626ebb 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import { + PurposeRiskAnalysisFormId, RiskAnalysisFormId, RiskAnalysisId, RiskAnalysisMultiAnswerId, @@ -28,6 +29,15 @@ export const RiskAnalysisForm = z.object({ }); export type RiskAnalysisForm = z.infer; +export const PurposeRiskAnalysisForm = z.object({ + id: PurposeRiskAnalysisFormId, + riskAnalysisId: RiskAnalysisFormId.optional(), + version: z.string(), + singleAnswers: z.array(RiskAnalysisSingleAnswer), + multiAnswers: z.array(RiskAnalysisMultiAnswer), +}); +export type PurposeRiskAnalysisForm = z.infer; + export const RiskAnalysis = z.object({ id: RiskAnalysisId, name: z.string(), From 4d8e352c8b5a70de45a11f5ec52ee1f982a0f3e6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 16:35:46 +0200 Subject: [PATCH 015/537] Fix typo --- packages/models/src/purpose/purpose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 9b8ae606a8..a479b2c20a 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -34,7 +34,7 @@ export const PurposeVersion = z.object({ id: PurposeVersionId, state: PurposeVersionState, expectedApprovalDate: z.coerce.date().optional(), - riskAnalysys: PurposeVersionDocument.optional(), + riskAnalysis: PurposeVersionDocument.optional(), dailyCalls: z.number(), rejectionReason: z.string().optional(), createdAt: z.coerce.date(), From bdceef1fe54eed42c49c9f5791963de3433b799f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 17:42:12 +0200 Subject: [PATCH 016/537] Fix typo --- packages/models/src/purpose/purpose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index a479b2c20a..5de0475456 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -49,7 +49,7 @@ export const Purpose = z.object({ eserviceId: EServiceId, consumerId: TenantId, versions: z.array(PurposeVersion), - suspendedByCondumer: z.boolean().optional(), + suspendedByConsumer: z.boolean().optional(), suspendedByProducer: z.boolean().optional(), title: z.string(), description: z.string(), From b40285db93d58270d3e05522ee4870bedaeca403 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:19:58 +0200 Subject: [PATCH 017/537] Scaffold events v1 --- .../models/proto/v1/purpose/purpose.proto | 2 +- .../proto/v1/purpose/riskAnalysis.proto | 2 +- packages/models/src/brandedIds.ts | 1 + packages/models/src/index.ts | 2 + .../src/purpose/protobufConverterFromV1.ts | 94 +++++++++++ .../src/purpose/protobufConverterToV1.ts | 65 ++++++++ packages/models/src/purpose/purposeEvents.ts | 147 ++++++++++++++++++ .../models/src/risk-analysis/riskAnalysis.ts | 4 +- 8 files changed, 313 insertions(+), 4 deletions(-) create mode 100644 packages/models/src/purpose/protobufConverterFromV1.ts create mode 100644 packages/models/src/purpose/protobufConverterToV1.ts create mode 100644 packages/models/src/purpose/purposeEvents.ts diff --git a/packages/models/proto/v1/purpose/purpose.proto b/packages/models/proto/v1/purpose/purpose.proto index 073b34f307..9c4f19e48f 100644 --- a/packages/models/proto/v1/purpose/purpose.proto +++ b/packages/models/proto/v1/purpose/purpose.proto @@ -13,7 +13,7 @@ message PurposeV1 { repeated PurposeVersionV1 versions = 7; required string title = 8; required string description = 9; - optional RiskAnalysisFormV1 riskAnalysisForm = 10; + optional PurposeRiskAnalysisFormV1 riskAnalysisForm = 10; required int64 createdAt = 11; optional int64 updatedAt = 12; optional bool isFreeOfCharge = 13; diff --git a/packages/models/proto/v1/purpose/riskAnalysis.proto b/packages/models/proto/v1/purpose/riskAnalysis.proto index ed1ad5f296..333a39c29a 100644 --- a/packages/models/proto/v1/purpose/riskAnalysis.proto +++ b/packages/models/proto/v1/purpose/riskAnalysis.proto @@ -2,7 +2,7 @@ syntax = "proto2"; package purpose; -message RiskAnalysisFormV1 { +message PurposeRiskAnalysisFormV1 { required string id = 1; required string version = 2; repeated RiskAnalysisSingleAnswerV1 singleAnswers = 3; diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index b43b92657e..8a03daf41e 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -80,6 +80,7 @@ type IDS = | RiskAnalysisFormId | RiskAnalysisId | PurposeId + | PurposeVersionId | PurposeVersionDocumentId | PurposeRiskAnalysisFormId; diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 1016f7878c..a5c7edcbf0 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -57,3 +57,5 @@ export * from "./gen/v1/tenant/events.js"; export * from "./gen/v1/tenant/tenant.js"; export * from "./gen/v2/eservice/eservice.js"; export * from "./gen/v2/eservice/events.js"; +export * from "./gen/v1/purpose/purpose.js"; +export * from "./gen/v1/purpose/events.js"; diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts new file mode 100644 index 0000000000..3568fa13af --- /dev/null +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -0,0 +1,94 @@ +import { + PurposeStateV1, + PurposeVersionDocumentV1, + PurposeVersionV1, + PurposeRiskAnalysisFormV1, + PurposeV1, +} from "../gen/v1/purpose/purpose.js"; +import { unsafeBrandId } from "../brandedIds.js"; +import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; +import { + Purpose, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, +} from "./purpose.js"; + +export const fromPurposeVersionStateV1 = ( + input: PurposeStateV1 +): PurposeVersionState => { + switch (input) { + case PurposeStateV1.DRAFT: + return purposeVersionState.draft; + case PurposeStateV1.ACTIVE: + return purposeVersionState.active; + case PurposeStateV1.SUSPENDED: + return purposeVersionState.suspended; + case PurposeStateV1.ARCHIVED: + return purposeVersionState.archived; + case PurposeStateV1.WAITING_FOR_APPROVAL: + return purposeVersionState.waitingForApproval; + case PurposeStateV1.REJECTED: + return purposeVersionState.rejected; + case PurposeStateV1.UNSPECIFIED$: { + throw new Error("Unspecified purpose version state"); + } + } +}; + +export const fromPurposeVersionDocumentV1 = ( + input: PurposeVersionDocumentV1 +): PurposeVersionDocument => ({ + ...input, + id: unsafeBrandId(input.id), + createdAt: new Date(Number(input.createdAt)), +}); + +export const fromPurposeVersionV1 = ( + input: PurposeVersionV1 +): PurposeVersion => ({ + ...input, + id: unsafeBrandId(input.id), + state: fromPurposeVersionStateV1(input.state), + expectedApprovalDate: input.expectedApprovalDate + ? new Date(Number(input.expectedApprovalDate)) + : undefined, + riskAnalysis: input.riskAnalysis + ? fromPurposeVersionDocumentV1(input.riskAnalysis) + : undefined, + createdAt: new Date(Number(input.createdAt)), + updatedAt: new Date(Number(input.updatedAt)), + firstActivationAt: new Date(Number(input.updatedAt)), + suspendedAt: new Date(Number(input.suspendedAt)), +}); + +export const fromPurposeRiskAnalysisFormV1 = ( + input: PurposeRiskAnalysisFormV1 +): PurposeRiskAnalysisForm => ({ + ...input, + id: unsafeBrandId(input.id), + riskAnalysisId: undefined, // TO DO + singleAnswers: input.singleAnswers.map((a) => ({ + ...a, + id: unsafeBrandId(a.id), + })), + multiAnswers: input.multiAnswers.map((a) => ({ + ...a, + id: unsafeBrandId(a.id), + })), +}); + +export const fromPurposeV1 = (input: PurposeV1): Purpose => ({ + ...input, + id: unsafeBrandId(input.id), + eserviceId: unsafeBrandId(input.eserviceId), + consumerId: unsafeBrandId(input.consumerId), + versions: input.versions.map(fromPurposeVersionV1), + isFreeOfCharge: input.isFreeOfCharge || true, + createdAt: new Date(Number(input.createdAt)), + updatedAt: new Date(Number(input.updatedAt)), + riskAnalysisForm: input.riskAnalysisForm + ? fromPurposeRiskAnalysisFormV1(input.riskAnalysisForm) + : undefined, +}); diff --git a/packages/models/src/purpose/protobufConverterToV1.ts b/packages/models/src/purpose/protobufConverterToV1.ts new file mode 100644 index 0000000000..ceb80a5803 --- /dev/null +++ b/packages/models/src/purpose/protobufConverterToV1.ts @@ -0,0 +1,65 @@ +import { match } from "ts-pattern"; +import { + PurposeStateV1, + PurposeV1, + PurposeVersionDocumentV1, + PurposeVersionV1, +} from "../gen/v1/purpose/purpose.js"; + +import { + Purpose, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, +} from "./purpose.js"; + +export const toPurposeVersionStateV1 = ( + input: PurposeVersionState +): PurposeStateV1 => + match(input) + .with(purposeVersionState.draft, () => PurposeStateV1.DRAFT) + .with(purposeVersionState.active, () => PurposeStateV1.ACTIVE) + .with(purposeVersionState.suspended, () => PurposeStateV1.SUSPENDED) + .with(purposeVersionState.archived, () => PurposeStateV1.ARCHIVED) + .with( + purposeVersionState.waitingForApproval, + () => PurposeStateV1.WAITING_FOR_APPROVAL + ) + .with(purposeVersionState.rejected, () => PurposeStateV1.REJECTED) + .exhaustive(); + +export const toPurposeVersionDocumentV1 = ( + input: PurposeVersionDocument +): PurposeVersionDocumentV1 => ({ + ...input, + createdAt: BigInt(input.createdAt.getTime()), +}); + +export const toPurposeVersionV1 = ( + input: PurposeVersion +): PurposeVersionV1 => ({ + ...input, + state: toPurposeVersionStateV1(input.state), + expectedApprovalDate: input.expectedApprovalDate + ? BigInt(input.expectedApprovalDate.getTime()) + : undefined, + createdAt: BigInt(input.createdAt.getTime()), + updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, + firstActivationAt: input.firstActivationAt + ? BigInt(input.firstActivationAt.getTime()) + : undefined, + suspendedAt: input.suspendedAt + ? BigInt(input.suspendedAt.getTime()) + : undefined, + riskAnalysis: input.riskAnalysis + ? toPurposeVersionDocumentV1(input.riskAnalysis) + : undefined, +}); + +export const toPurposeV1 = (input: Purpose): PurposeV1 => ({ + ...input, + versions: input.versions.map(toPurposeVersionV1), + createdAt: BigInt(input.createdAt.getTime()), + updatedAt: BigInt(input.updatedAt.getTime()), +}); diff --git a/packages/models/src/purpose/purposeEvents.ts b/packages/models/src/purpose/purposeEvents.ts new file mode 100644 index 0000000000..305e72bf8a --- /dev/null +++ b/packages/models/src/purpose/purposeEvents.ts @@ -0,0 +1,147 @@ +import { match } from "ts-pattern"; +import { z } from "zod"; +import { EventEnvelope } from "../events/events.js"; +import { + PurposeCreatedV1, + PurposeDeletedV1, + PurposeUpdatedV1, + PurposeVersionActivatedV1, + PurposeVersionArchivedV1, + PurposeVersionCreatedV1, + PurposeVersionDeletedV1, + PurposeVersionRejectedV1, + PurposeVersionSuspendedV1, + PurposeVersionUpdatedV1, + PurposeVersionWaitedForApprovalV1, +} from "../gen/v1/purpose/events.js"; +import { protobufDecoder } from "../protobuf/protobuf.js"; + +export function purposeEventToBinaryData(event: PurposeEvent): Uint8Array { + return match(event) + .with({ event_version: 1 }, purposeEventToBinaryDataV1) + .exhaustive(); +} + +export function purposeEventToBinaryDataV1(event: PurposeEvent): Uint8Array { + return match(event) + .with({ type: "PurposeCreated" }, ({ data }) => + PurposeCreatedV1.toBinary(data) + ) + .with({ type: "PurposeVersionCreated" }, ({ data }) => + PurposeVersionCreatedV1.toBinary(data) + ) + .with({ type: "PurposeUpdated" }, ({ data }) => + PurposeUpdatedV1.toBinary(data) + ) + .with({ type: "PurposeVersionUpdated" }, ({ data }) => + PurposeVersionUpdatedV1.toBinary(data) + ) + .with({ type: "PurposeVersionActivated" }, ({ data }) => + PurposeVersionActivatedV1.toBinary(data) + ) + .with({ type: "PurposeVersionRejected" }, ({ data }) => + PurposeVersionRejectedV1.toBinary(data) + ) + .with({ type: "PurposeVersionSuspended" }, ({ data }) => + PurposeVersionSuspendedV1.toBinary(data) + ) + .with({ type: "PurposeVersionArchived" }, ({ data }) => + PurposeVersionArchivedV1.toBinary(data) + ) + .with({ type: "PurposeVersionWaitedForApproval" }, ({ data }) => + PurposeVersionWaitedForApprovalV1.toBinary(data) + ) + .with({ type: "PurposeDeleted" }, ({ data }) => + PurposeDeletedV1.toBinary(data) + ) + .with({ type: "PurposeVersionDeleted" }, ({ data }) => + PurposeVersionDeletedV1.toBinary(data) + ) + .exhaustive(); +} + +export const PurposeEventV1 = z.discriminatedUnion("type", [ + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeCreated"), + data: protobufDecoder(PurposeCreatedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeUpdated"), + data: protobufDecoder(PurposeUpdatedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionWaitedForApproval"), + data: protobufDecoder(PurposeVersionWaitedForApprovalV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionActivated"), + data: protobufDecoder(PurposeVersionActivatedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionCreated"), + data: protobufDecoder(PurposeVersionCreatedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionSuspended"), + data: protobufDecoder(PurposeVersionSuspendedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionArchived"), + data: protobufDecoder(PurposeVersionArchivedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionUpdated"), + data: protobufDecoder(PurposeVersionUpdatedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionDeleted"), + data: protobufDecoder(PurposeVersionDeletedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeDeleted"), + data: protobufDecoder(PurposeDeletedV1), + }), + z.object({ + event_version: z.literal(1), + type: z.literal("PurposeVersionRejected"), + data: protobufDecoder(PurposeVersionRejectedV1), + }), +]); +export type PurposeEventV1 = z.infer; + +const eventV1 = z + .object({ + event_version: z.literal(1), + }) + .passthrough(); + +export const PurposeEvent = z + .discriminatedUnion("event_version", [eventV1]) + .transform((obj, ctx) => { + const res = match(obj) + .with({ event_version: 1 }, () => PurposeEventV1.safeParse(obj)) + .exhaustive(); + + if (!res.success) { + res.error.issues.forEach(ctx.addIssue); + return z.NEVER; + } + return res.data; + }); +export type PurposeEvent = z.infer; + +export const PurposeEventEnvelopeV1 = EventEnvelope(PurposeEventV1); +export type PurposeEventEnvelopeV1 = z.infer; + +export const PurposeEventEnvelope = EventEnvelope(PurposeEvent); +export type PurposeEventEnvelope = z.infer; diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 1f79626ebb..11e68f3692 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -31,12 +31,12 @@ export type RiskAnalysisForm = z.infer; export const PurposeRiskAnalysisForm = z.object({ id: PurposeRiskAnalysisFormId, - riskAnalysisId: RiskAnalysisFormId.optional(), + riskAnalysisId: RiskAnalysisId.optional(), version: z.string(), singleAnswers: z.array(RiskAnalysisSingleAnswer), multiAnswers: z.array(RiskAnalysisMultiAnswer), }); -export type PurposeRiskAnalysisForm = z.infer; +export type PurposeRiskAnalysisForm = z.infer; export const RiskAnalysis = z.object({ id: RiskAnalysisId, From 2ce50a402e80b450e33947c03326c4baad633ce2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:29:01 +0200 Subject: [PATCH 018/537] Add export --- packages/models/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index a5c7edcbf0..b0d233697b 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -30,6 +30,9 @@ export * from "./tenant/tenant.js"; export * from "./tenant/tenantEvents.js"; export * from "./purpose/purpose.js"; +export * from "./purpose/purposeEvents.js"; +export * from "./purpose/protobufConverterFromV1.js"; +export * from "./purpose/protobufConverterToV1.js"; export * from "./user/user.js"; From 2c2cd04e60b44893dda6c54a8b94b40421d5f5d7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:34:27 +0200 Subject: [PATCH 019/537] Fix import --- packages/models/src/purpose/protobufConverterFromV1.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index 3568fa13af..af29f13dc7 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -1,12 +1,12 @@ import { PurposeStateV1, + PurposeV1, PurposeVersionDocumentV1, PurposeVersionV1, - PurposeRiskAnalysisFormV1, - PurposeV1, } from "../gen/v1/purpose/purpose.js"; import { unsafeBrandId } from "../brandedIds.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; +import { PurposeRiskAnalysisFormV1 } from "../gen/v1/purpose/riskAnalysis.js"; import { Purpose, PurposeVersion, From ab15e439fd92122dbbfcfb2b7687a389d6cc7ccf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:36:25 +0200 Subject: [PATCH 020/537] Fix --- packages/models/src/risk-analysis/riskAnalysis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 1f79626ebb..11e68f3692 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -31,12 +31,12 @@ export type RiskAnalysisForm = z.infer; export const PurposeRiskAnalysisForm = z.object({ id: PurposeRiskAnalysisFormId, - riskAnalysisId: RiskAnalysisFormId.optional(), + riskAnalysisId: RiskAnalysisId.optional(), version: z.string(), singleAnswers: z.array(RiskAnalysisSingleAnswer), multiAnswers: z.array(RiskAnalysisMultiAnswer), }); -export type PurposeRiskAnalysisForm = z.infer; +export type PurposeRiskAnalysisForm = z.infer; export const RiskAnalysis = z.object({ id: RiskAnalysisId, From c24ebae30c05a2c3daaf007bdecab4312c3113f5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:36:56 +0200 Subject: [PATCH 021/537] Fix --- packages/models/src/brandedIds.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index b43b92657e..8a03daf41e 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -80,6 +80,7 @@ type IDS = | RiskAnalysisFormId | RiskAnalysisId | PurposeId + | PurposeVersionId | PurposeVersionDocumentId | PurposeRiskAnalysisFormId; From 59b08e91bf2d183ae3058666729b4157d309ab4d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:45:17 +0200 Subject: [PATCH 022/537] Fix --- packages/models/src/purpose/protobufConverterFromV1.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index af29f13dc7..ae8846cf0d 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -68,7 +68,9 @@ export const fromPurposeRiskAnalysisFormV1 = ( ): PurposeRiskAnalysisForm => ({ ...input, id: unsafeBrandId(input.id), - riskAnalysisId: undefined, // TO DO + riskAnalysisId: input.riskAnalysisId + ? unsafeBrandId(input.riskAnalysisId) + : undefined, singleAnswers: input.singleAnswers.map((a) => ({ ...a, id: unsafeBrandId(a.id), From b9398e3c23c8522617445360e258551962c403a0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:47:26 +0200 Subject: [PATCH 023/537] Temporary fix and add comment --- packages/models/src/risk-analysis/riskAnalysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 11e68f3692..fe67475b7b 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -31,7 +31,7 @@ export type RiskAnalysisForm = z.infer; export const PurposeRiskAnalysisForm = z.object({ id: PurposeRiskAnalysisFormId, - riskAnalysisId: RiskAnalysisId.optional(), + riskAnalysisId: z.string().optional(), // TO DO: it should be RiskAnalysis.Id.optional() version: z.string(), singleAnswers: z.array(RiskAnalysisSingleAnswer), multiAnswers: z.array(RiskAnalysisMultiAnswer), From b973cc58f3d8ca981a52403a7bb8fdcb122efe90 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 18:47:59 +0200 Subject: [PATCH 024/537] Fix end of file --- packages/models/proto/v1/purpose/events.proto | 2 +- packages/models/proto/v1/purpose/purpose.proto | 2 +- packages/models/proto/v1/purpose/riskAnalysis.proto | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/models/proto/v1/purpose/events.proto b/packages/models/proto/v1/purpose/events.proto index 11bd8d2961..af402924b0 100644 --- a/packages/models/proto/v1/purpose/events.proto +++ b/packages/models/proto/v1/purpose/events.proto @@ -50,4 +50,4 @@ message PurposeDeletedV1 { message PurposeVersionDeletedV1 { required string purposeId = 1; required string versionId = 2; -} \ No newline at end of file +} diff --git a/packages/models/proto/v1/purpose/purpose.proto b/packages/models/proto/v1/purpose/purpose.proto index 9c4f19e48f..a08143175b 100644 --- a/packages/models/proto/v1/purpose/purpose.proto +++ b/packages/models/proto/v1/purpose/purpose.proto @@ -47,4 +47,4 @@ message PurposeVersionDocumentV1 { required string contentType = 2; required string path = 3; required int64 createdAt = 4; -} \ No newline at end of file +} diff --git a/packages/models/proto/v1/purpose/riskAnalysis.proto b/packages/models/proto/v1/purpose/riskAnalysis.proto index 333a39c29a..4f239c68fc 100644 --- a/packages/models/proto/v1/purpose/riskAnalysis.proto +++ b/packages/models/proto/v1/purpose/riskAnalysis.proto @@ -20,4 +20,4 @@ message RiskAnalysisMultiAnswerV1 { required string id = 1; required string key = 2; repeated string values = 3; -} \ No newline at end of file +} From 7840aa741fa3a8a0b4d1d6afa8457601051fd371 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:02:35 +0200 Subject: [PATCH 025/537] Add protobuf definition --- packages/models/proto/v2/purpose/events.proto | 82 +++++++++++++++++++ .../models/proto/v2/purpose/purpose.proto | 50 +++++++++++ .../proto/v2/purpose/riskAnalysis.proto | 23 ++++++ 3 files changed, 155 insertions(+) create mode 100644 packages/models/proto/v2/purpose/events.proto create mode 100644 packages/models/proto/v2/purpose/purpose.proto create mode 100644 packages/models/proto/v2/purpose/riskAnalysis.proto diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto new file mode 100644 index 0000000000..548ef4eeb6 --- /dev/null +++ b/packages/models/proto/v2/purpose/events.proto @@ -0,0 +1,82 @@ +syntax = "proto2"; + +package purpose.v2; + +import "v2/purpose/purpose.proto"; + +message PurposeAddedV2 { + required PurposeV2 purpose = 1; +} + +message DraftPurposeUpdatedV2 { + required PurposeV2 purpose = 1; +} + +message PurposeWaitingForApprovalV2 { + required PurposeV2 purpose = 1; +} + +message PurposeActivatedV2 { + required PurposeV2 purpose = 1; +} + +message DraftPurposeDeletedV2 { + required PurposeV2 purpose = 1; +} + +message WaitingForApprovalPurposeDeletedV2 { + required PurposeV2 purpose = 1; +} + +message NewPurposeVersionActivatedV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeVersionActivatedV2 { + required PurposeV2 purpose = 1; +} + +message PurposeVersionUnsuspenedByProducerV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeVersionUnsuspenedByConsumerV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeVersionSuspenedByProducerV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeVersionSuspenedByConsumerV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message NewPurposeVersionWaitingForApprovalV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeVersionOverQuotaUnsuspendedV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeArchivedV2 { + required PurposeV2 purpose = 1; +} + +message WaitingForApprovalPurposeVersionDeletedV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} + +message PurposeVersionRejectedV2 { + required string versionId = 1; + required PurposeV2 purpose = 2; +} diff --git a/packages/models/proto/v2/purpose/purpose.proto b/packages/models/proto/v2/purpose/purpose.proto new file mode 100644 index 0000000000..6e531b711f --- /dev/null +++ b/packages/models/proto/v2/purpose/purpose.proto @@ -0,0 +1,50 @@ +syntax = "proto2"; + +package purpose.v2; + +import "v2/purpose/riskAnalysis.proto"; + +message PurposeV2 { + required string id = 1; + required string eserviceId = 2; + required string consumerId = 3; + optional bool suspendedByConsumer = 5; + optional bool suspendedByProducer = 6; + repeated PurposeVersionV2 versions = 7; + required string title = 8; + required string description = 9; + optional PurposeRiskAnalysisFormV2 riskAnalysisForm = 10; + required int64 createdAt = 11; + optional int64 updatedAt = 12; + optional bool isFreeOfCharge = 13; + optional string freeOfChargeReason = 14; +} + +message PurposeVersionV2 { + required string id = 1; + required PurposeStateV2 state = 2; + optional PurposeVersionDocumentV2 riskAnalysis = 3; + required int32 dailyCalls = 4; + required int64 createdAt = 5; + optional int64 updatedAt = 6; + optional int64 firstActivationAt = 7; + optional int64 expectedApprovalDate = 8; + optional int64 suspendedAt = 9; + optional string rejectionReason = 10; +} + +enum PurposeStateV2 { + DRAFT = 1; + ACTIVE = 2; + SUSPENDED = 3; + WAITING_FOR_APPROVAL = 4; + ARCHIVED = 5; + REJECTED = 6; +} + +message PurposeVersionDocumentV2 { + required string id = 1; + required string contentType = 2; + required string path = 3; + required int64 createdAt = 4; +} diff --git a/packages/models/proto/v2/purpose/riskAnalysis.proto b/packages/models/proto/v2/purpose/riskAnalysis.proto new file mode 100644 index 0000000000..3e0111ca69 --- /dev/null +++ b/packages/models/proto/v2/purpose/riskAnalysis.proto @@ -0,0 +1,23 @@ +syntax = "proto2"; + +package purpose.v2; + +message PurposeRiskAnalysisFormV2 { + required string id = 1; + required string version = 2; + repeated RiskAnalysisSingleAnswerV1 singleAnswers = 3; + repeated RiskAnalysisMultiAnswerV1 multiAnswers = 4; + optional string riskAnalysisId = 5; +} + +message RiskAnalysisSingleAnswerV2 { + required string id = 1; + required string key = 2; + optional string value = 3; +} + +message RiskAnalysisMultiAnswerV2 { + required string id = 1; + required string key = 2; + repeated string values = 3; +} From 4194ff0846f5fe00dc400af4305ba82a08d5600e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:15:55 +0200 Subject: [PATCH 026/537] Use proto3 --- packages/models/proto/v2/purpose/events.proto | 2 +- packages/models/proto/v2/purpose/purpose.proto | 2 +- packages/models/proto/v2/purpose/riskAnalysis.proto | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto index 548ef4eeb6..dc036a5e37 100644 --- a/packages/models/proto/v2/purpose/events.proto +++ b/packages/models/proto/v2/purpose/events.proto @@ -1,4 +1,4 @@ -syntax = "proto2"; +syntax = "proto3"; package purpose.v2; diff --git a/packages/models/proto/v2/purpose/purpose.proto b/packages/models/proto/v2/purpose/purpose.proto index 6e531b711f..aba90eaeaf 100644 --- a/packages/models/proto/v2/purpose/purpose.proto +++ b/packages/models/proto/v2/purpose/purpose.proto @@ -1,4 +1,4 @@ -syntax = "proto2"; +syntax = "proto3"; package purpose.v2; diff --git a/packages/models/proto/v2/purpose/riskAnalysis.proto b/packages/models/proto/v2/purpose/riskAnalysis.proto index 3e0111ca69..38bb4b685f 100644 --- a/packages/models/proto/v2/purpose/riskAnalysis.proto +++ b/packages/models/proto/v2/purpose/riskAnalysis.proto @@ -1,4 +1,4 @@ -syntax = "proto2"; +syntax = "proto3"; package purpose.v2; From 57a402893217fd7980a86ff03ba67616858bf383 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:16:48 +0200 Subject: [PATCH 027/537] Fix --- packages/models/proto/v2/purpose/riskAnalysis.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models/proto/v2/purpose/riskAnalysis.proto b/packages/models/proto/v2/purpose/riskAnalysis.proto index 38bb4b685f..13b619709a 100644 --- a/packages/models/proto/v2/purpose/riskAnalysis.proto +++ b/packages/models/proto/v2/purpose/riskAnalysis.proto @@ -5,8 +5,8 @@ package purpose.v2; message PurposeRiskAnalysisFormV2 { required string id = 1; required string version = 2; - repeated RiskAnalysisSingleAnswerV1 singleAnswers = 3; - repeated RiskAnalysisMultiAnswerV1 multiAnswers = 4; + repeated RiskAnalysisSingleAnswerV2 singleAnswers = 3; + repeated RiskAnalysisMultiAnswerV2 multiAnswers = 4; optional string riskAnalysisId = 5; } From d9eff7f071b3a4cf6cbdbe1ddf2b4ce2781b4c19 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:20:02 +0200 Subject: [PATCH 028/537] Remove required --- packages/models/proto/v2/purpose/events.proto | 52 +++++++++---------- .../models/proto/v2/purpose/purpose.proto | 28 +++++----- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto index dc036a5e37..5471f5da50 100644 --- a/packages/models/proto/v2/purpose/events.proto +++ b/packages/models/proto/v2/purpose/events.proto @@ -5,78 +5,78 @@ package purpose.v2; import "v2/purpose/purpose.proto"; message PurposeAddedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message DraftPurposeUpdatedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message PurposeWaitingForApprovalV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message PurposeActivatedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message DraftPurposeDeletedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message WaitingForApprovalPurposeDeletedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message NewPurposeVersionActivatedV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionActivatedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message PurposeVersionUnsuspenedByProducerV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionUnsuspenedByConsumerV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionSuspenedByProducerV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionSuspenedByConsumerV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message NewPurposeVersionWaitingForApprovalV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionOverQuotaUnsuspendedV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeArchivedV2 { - required PurposeV2 purpose = 1; + PurposeV2 purpose = 1; } message WaitingForApprovalPurposeVersionDeletedV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionRejectedV2 { - required string versionId = 1; - required PurposeV2 purpose = 2; + string versionId = 1; + PurposeV2 purpose = 2; } diff --git a/packages/models/proto/v2/purpose/purpose.proto b/packages/models/proto/v2/purpose/purpose.proto index aba90eaeaf..4dd7d1927b 100644 --- a/packages/models/proto/v2/purpose/purpose.proto +++ b/packages/models/proto/v2/purpose/purpose.proto @@ -5,27 +5,27 @@ package purpose.v2; import "v2/purpose/riskAnalysis.proto"; message PurposeV2 { - required string id = 1; - required string eserviceId = 2; - required string consumerId = 3; + string id = 1; + string eserviceId = 2; + string consumerId = 3; optional bool suspendedByConsumer = 5; optional bool suspendedByProducer = 6; repeated PurposeVersionV2 versions = 7; - required string title = 8; - required string description = 9; + string title = 8; + string description = 9; optional PurposeRiskAnalysisFormV2 riskAnalysisForm = 10; - required int64 createdAt = 11; + int64 createdAt = 11; optional int64 updatedAt = 12; optional bool isFreeOfCharge = 13; optional string freeOfChargeReason = 14; } message PurposeVersionV2 { - required string id = 1; - required PurposeStateV2 state = 2; + string id = 1; + PurposeStateV2 state = 2; optional PurposeVersionDocumentV2 riskAnalysis = 3; - required int32 dailyCalls = 4; - required int64 createdAt = 5; + int32 dailyCalls = 4; + int64 createdAt = 5; optional int64 updatedAt = 6; optional int64 firstActivationAt = 7; optional int64 expectedApprovalDate = 8; @@ -43,8 +43,8 @@ enum PurposeStateV2 { } message PurposeVersionDocumentV2 { - required string id = 1; - required string contentType = 2; - required string path = 3; - required int64 createdAt = 4; + string id = 1; + string contentType = 2; + string path = 3; + int64 createdAt = 4; } From 92661d8e2e751eacefa2fc6929ea1319c37e0874 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:21:14 +0200 Subject: [PATCH 029/537] Remove required --- packages/models/proto/v2/purpose/riskAnalysis.proto | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/models/proto/v2/purpose/riskAnalysis.proto b/packages/models/proto/v2/purpose/riskAnalysis.proto index 13b619709a..db0ba6efc4 100644 --- a/packages/models/proto/v2/purpose/riskAnalysis.proto +++ b/packages/models/proto/v2/purpose/riskAnalysis.proto @@ -3,21 +3,21 @@ syntax = "proto3"; package purpose.v2; message PurposeRiskAnalysisFormV2 { - required string id = 1; - required string version = 2; + string id = 1; + string version = 2; repeated RiskAnalysisSingleAnswerV2 singleAnswers = 3; repeated RiskAnalysisMultiAnswerV2 multiAnswers = 4; optional string riskAnalysisId = 5; } message RiskAnalysisSingleAnswerV2 { - required string id = 1; - required string key = 2; + string id = 1; + string key = 2; optional string value = 3; } message RiskAnalysisMultiAnswerV2 { - required string id = 1; - required string key = 2; + string id = 1; + string key = 2; repeated string values = 3; } From b52c3131fbbfb6fd4cc50f5a76e5f21972fa1f04 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:23:29 +0200 Subject: [PATCH 030/537] Adjust numbers in proto enum --- packages/models/proto/v2/purpose/purpose.proto | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/models/proto/v2/purpose/purpose.proto b/packages/models/proto/v2/purpose/purpose.proto index 4dd7d1927b..f7f467b182 100644 --- a/packages/models/proto/v2/purpose/purpose.proto +++ b/packages/models/proto/v2/purpose/purpose.proto @@ -34,12 +34,12 @@ message PurposeVersionV2 { } enum PurposeStateV2 { - DRAFT = 1; - ACTIVE = 2; - SUSPENDED = 3; - WAITING_FOR_APPROVAL = 4; - ARCHIVED = 5; - REJECTED = 6; + DRAFT = 0; + ACTIVE = 1; + SUSPENDED = 2; + WAITING_FOR_APPROVAL = 3; + ARCHIVED = 4; + REJECTED = 5; } message PurposeVersionDocumentV2 { From 014eac0d77440ed9efdc1d658fc3bb8227beb4b0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:29:27 +0200 Subject: [PATCH 031/537] Add events v2 --- packages/models/src/index.ts | 2 + packages/models/src/purpose/purposeEvents.ts | 177 ++++++++++++++++++- 2 files changed, 177 insertions(+), 2 deletions(-) diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index b0d233697b..43b015c4a4 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -62,3 +62,5 @@ export * from "./gen/v2/eservice/eservice.js"; export * from "./gen/v2/eservice/events.js"; export * from "./gen/v1/purpose/purpose.js"; export * from "./gen/v1/purpose/events.js"; +export * from "./gen/v2/purpose/purpose.js"; +export * from "./gen/v2/purpose/events.js"; diff --git a/packages/models/src/purpose/purposeEvents.ts b/packages/models/src/purpose/purposeEvents.ts index 305e72bf8a..9c057f06c5 100644 --- a/packages/models/src/purpose/purposeEvents.ts +++ b/packages/models/src/purpose/purposeEvents.ts @@ -15,14 +15,34 @@ import { PurposeVersionWaitedForApprovalV1, } from "../gen/v1/purpose/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; +import { + PurposeAddedV2, + DraftPurposeUpdatedV2, + PurposeWaitingForApprovalV2, + PurposeActivatedV2, + DraftPurposeDeletedV2, + WaitingForApprovalPurposeDeletedV2, + NewPurposeVersionActivatedV2, + PurposeVersionActivatedV2, + PurposeVersionUnsuspenedByProducerV2, + PurposeVersionUnsuspenedByConsumerV2, + PurposeVersionSuspenedByProducerV2, + PurposeVersionSuspenedByConsumerV2, + NewPurposeVersionWaitingForApprovalV2, + PurposeVersionOverQuotaUnsuspendedV2, + PurposeArchivedV2, + WaitingForApprovalPurposeVersionDeletedV2, + PurposeVersionRejectedV2, +} from "../gen/v2/purpose/events.js"; export function purposeEventToBinaryData(event: PurposeEvent): Uint8Array { return match(event) .with({ event_version: 1 }, purposeEventToBinaryDataV1) + .with({ event_version: 2 }, purposeEventToBinaryDataV2) .exhaustive(); } -export function purposeEventToBinaryDataV1(event: PurposeEvent): Uint8Array { +export function purposeEventToBinaryDataV1(event: PurposeEventV1): Uint8Array { return match(event) .with({ type: "PurposeCreated" }, ({ data }) => PurposeCreatedV1.toBinary(data) @@ -60,6 +80,60 @@ export function purposeEventToBinaryDataV1(event: PurposeEvent): Uint8Array { .exhaustive(); } +export function purposeEventToBinaryDataV2(event: PurposeEventV2): Uint8Array { + return match(event) + .with({ type: "PurposeAdded" }, ({ data }) => PurposeAddedV2.toBinary(data)) + .with({ type: "DraftPurposeUpdated" }, ({ data }) => + DraftPurposeUpdatedV2.toBinary(data) + ) + .with({ type: "PurposeWaitingForApproval" }, ({ data }) => + PurposeWaitingForApprovalV2.toBinary(data) + ) + .with({ type: "PurposeActivated" }, ({ data }) => + PurposeActivatedV2.toBinary(data) + ) + .with({ type: "DraftPurposeDeleted" }, ({ data }) => + DraftPurposeDeletedV2.toBinary(data) + ) + .with({ type: "WaitingForApprovalPurposeDeleted" }, ({ data }) => + WaitingForApprovalPurposeDeletedV2.toBinary(data) + ) + .with({ type: "NewPurposeVersionActivated" }, ({ data }) => + NewPurposeVersionActivatedV2.toBinary(data) + ) + .with({ type: "PurposeVersionActivated" }, ({ data }) => + PurposeVersionActivatedV2.toBinary(data) + ) + .with({ type: "PurposeVersionUnsuspenedByProducer" }, ({ data }) => + PurposeVersionUnsuspenedByProducerV2.toBinary(data) + ) + .with({ type: "PurposeVersionUnsuspenedByConsumer" }, ({ data }) => + PurposeVersionUnsuspenedByConsumerV2.toBinary(data) + ) + .with({ type: "PurposeVersionSuspenedByProducer" }, ({ data }) => + PurposeVersionSuspenedByProducerV2.toBinary(data) + ) + .with({ type: "PurposeVersionSuspenedByConsumer" }, ({ data }) => + PurposeVersionSuspenedByConsumerV2.toBinary(data) + ) + .with({ type: "NewPurposeVersionWaitingForApproval" }, ({ data }) => + NewPurposeVersionWaitingForApprovalV2.toBinary(data) + ) + .with({ type: "PurposeVersionOverQuotaUnsuspended" }, ({ data }) => + PurposeVersionOverQuotaUnsuspendedV2.toBinary(data) + ) + .with({ type: "PurposeArchived" }, ({ data }) => + PurposeArchivedV2.toBinary(data) + ) + .with({ type: "WaitingForApprovalPurposeVersionDeleted" }, ({ data }) => + WaitingForApprovalPurposeVersionDeletedV2.toBinary(data) + ) + .with({ type: "PurposeVersionRejected" }, ({ data }) => + PurposeVersionRejectedV2.toBinary(data) + ) + .exhaustive(); +} + export const PurposeEventV1 = z.discriminatedUnion("type", [ z.object({ event_version: z.literal(1), @@ -119,17 +193,113 @@ export const PurposeEventV1 = z.discriminatedUnion("type", [ ]); export type PurposeEventV1 = z.infer; +export const PurposeEventV2 = z.discriminatedUnion("type", [ + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeAdded"), + data: protobufDecoder(PurposeAddedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("DraftPurposeUpdated"), + data: protobufDecoder(DraftPurposeUpdatedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeWaitingForApproval"), + data: protobufDecoder(PurposeWaitingForApprovalV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeActivated"), + data: protobufDecoder(PurposeActivatedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("DraftPurposeDeleted"), + data: protobufDecoder(DraftPurposeDeletedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("WaitingForApprovalPurposeDeleted"), + data: protobufDecoder(WaitingForApprovalPurposeDeletedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("NewPurposeVersionActivated"), + data: protobufDecoder(NewPurposeVersionActivatedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionActivated"), + data: protobufDecoder(PurposeVersionActivatedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionUnsuspenedByProducer"), + data: protobufDecoder(PurposeVersionUnsuspenedByProducerV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionUnsuspenedByConsumer"), + data: protobufDecoder(PurposeVersionUnsuspenedByConsumerV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionSuspenedByProducer"), + data: protobufDecoder(PurposeVersionSuspenedByProducerV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionSuspenedByConsumer"), + data: protobufDecoder(PurposeVersionSuspenedByConsumerV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("NewPurposeVersionWaitingForApproval"), + data: protobufDecoder(NewPurposeVersionWaitingForApprovalV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionOverQuotaUnsuspended"), + data: protobufDecoder(PurposeVersionOverQuotaUnsuspendedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeArchived"), + data: protobufDecoder(PurposeArchivedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("WaitingForApprovalPurposeVersionDeleted"), + data: protobufDecoder(WaitingForApprovalPurposeVersionDeletedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeVersionRejected"), + data: protobufDecoder(PurposeVersionRejectedV2), + }), +]); +export type PurposeEventV2 = z.infer; + const eventV1 = z .object({ event_version: z.literal(1), }) .passthrough(); +const eventV2 = z + .object({ + event_version: z.literal(2), + }) + .passthrough(); + export const PurposeEvent = z - .discriminatedUnion("event_version", [eventV1]) + .discriminatedUnion("event_version", [eventV1, eventV2]) .transform((obj, ctx) => { const res = match(obj) .with({ event_version: 1 }, () => PurposeEventV1.safeParse(obj)) + .with({ event_version: 2 }, () => PurposeEventV2.safeParse(obj)) .exhaustive(); if (!res.success) { @@ -143,5 +313,8 @@ export type PurposeEvent = z.infer; export const PurposeEventEnvelopeV1 = EventEnvelope(PurposeEventV1); export type PurposeEventEnvelopeV1 = z.infer; +export const PurposeEventEnvelopeV2 = EventEnvelope(PurposeEventV2); +export type PurposeEventEnvelopeV2 = z.infer; + export const PurposeEventEnvelope = EventEnvelope(PurposeEvent); export type PurposeEventEnvelope = z.infer; From 281e80fbfa00d2adb7b1494c3b6e29e5faea02a7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 8 Apr 2024 19:48:09 +0200 Subject: [PATCH 032/537] Add v2 converters --- .../src/purpose/protobufConverterFromV2.ts | 93 +++++++++++++++++++ .../src/purpose/protobufConverterToV2.ts | 64 +++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 packages/models/src/purpose/protobufConverterFromV2.ts create mode 100644 packages/models/src/purpose/protobufConverterToV2.ts diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts new file mode 100644 index 0000000000..64ab55ea57 --- /dev/null +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -0,0 +1,93 @@ +import { unsafeBrandId } from "../brandedIds.js"; +import { + PurposeStateV2, + PurposeVersionDocumentV2, + PurposeVersionV2, + PurposeV2, +} from "../gen/v2/purpose/purpose.js"; +import { PurposeRiskAnalysisFormV2 } from "../gen/v2/purpose/riskAnalysis.js"; +import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; +import { + Purpose, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, +} from "./purpose.js"; + +export const fromPurposeVersionStateV2 = ( + input: PurposeStateV2 +): PurposeVersionState => { + switch (input) { + case PurposeStateV2.DRAFT: + return purposeVersionState.draft; + case PurposeStateV2.ACTIVE: + return purposeVersionState.active; + case PurposeStateV2.SUSPENDED: + return purposeVersionState.suspended; + case PurposeStateV2.ARCHIVED: + return purposeVersionState.archived; + case PurposeStateV2.WAITING_FOR_APPROVAL: + return purposeVersionState.waitingForApproval; + case PurposeStateV2.REJECTED: + return purposeVersionState.rejected; + } +}; + +export const fromPurposeVersionDocumentV2 = ( + input: PurposeVersionDocumentV2 +): PurposeVersionDocument => ({ + ...input, + id: unsafeBrandId(input.id), + createdAt: new Date(Number(input.createdAt)), +}); + +export const fromPurposeVersionV2 = ( + input: PurposeVersionV2 +): PurposeVersion => ({ + ...input, + id: unsafeBrandId(input.id), + state: fromPurposeVersionStateV2(input.state), + expectedApprovalDate: input.expectedApprovalDate + ? new Date(Number(input.expectedApprovalDate)) + : undefined, + riskAnalysis: input.riskAnalysis + ? fromPurposeVersionDocumentV2(input.riskAnalysis) + : undefined, + createdAt: new Date(Number(input.createdAt)), + updatedAt: new Date(Number(input.updatedAt)), + firstActivationAt: new Date(Number(input.updatedAt)), + suspendedAt: new Date(Number(input.suspendedAt)), +}); + +export const fromPurposeRiskAnalysisFormV2 = ( + input: PurposeRiskAnalysisFormV2 +): PurposeRiskAnalysisForm => ({ + ...input, + id: unsafeBrandId(input.id), + riskAnalysisId: input.riskAnalysisId + ? unsafeBrandId(input.riskAnalysisId) + : undefined, + singleAnswers: input.singleAnswers.map((a) => ({ + ...a, + id: unsafeBrandId(a.id), + })), + multiAnswers: input.multiAnswers.map((a) => ({ + ...a, + id: unsafeBrandId(a.id), + })), +}); + +export const fromPurposeV2 = (input: PurposeV2): Purpose => ({ + ...input, + id: unsafeBrandId(input.id), + eserviceId: unsafeBrandId(input.eserviceId), + consumerId: unsafeBrandId(input.consumerId), + versions: input.versions.map(fromPurposeVersionV2), + isFreeOfCharge: input.isFreeOfCharge || true, + createdAt: new Date(Number(input.createdAt)), + updatedAt: new Date(Number(input.updatedAt)), + riskAnalysisForm: input.riskAnalysisForm + ? fromPurposeRiskAnalysisFormV2(input.riskAnalysisForm) + : undefined, +}); diff --git a/packages/models/src/purpose/protobufConverterToV2.ts b/packages/models/src/purpose/protobufConverterToV2.ts new file mode 100644 index 0000000000..4045a9fb1e --- /dev/null +++ b/packages/models/src/purpose/protobufConverterToV2.ts @@ -0,0 +1,64 @@ +import { match } from "ts-pattern"; +import { + PurposeStateV2, + PurposeV2, + PurposeVersionDocumentV2, + PurposeVersionV2, +} from "../gen/v2/purpose/purpose.js"; +import { + Purpose, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, +} from "./purpose.js"; + +export const toPurposeVersionStateV2 = ( + input: PurposeVersionState +): PurposeStateV2 => + match(input) + .with(purposeVersionState.draft, () => PurposeStateV2.DRAFT) + .with(purposeVersionState.active, () => PurposeStateV2.ACTIVE) + .with(purposeVersionState.suspended, () => PurposeStateV2.SUSPENDED) + .with(purposeVersionState.archived, () => PurposeStateV2.ARCHIVED) + .with( + purposeVersionState.waitingForApproval, + () => PurposeStateV2.WAITING_FOR_APPROVAL + ) + .with(purposeVersionState.rejected, () => PurposeStateV2.REJECTED) + .exhaustive(); + +export const toPurposeVersionDocumentV2 = ( + input: PurposeVersionDocument +): PurposeVersionDocumentV2 => ({ + ...input, + createdAt: BigInt(input.createdAt.getTime()), +}); + +export const toPurposeVersionV2 = ( + input: PurposeVersion +): PurposeVersionV2 => ({ + ...input, + state: toPurposeVersionStateV2(input.state), + expectedApprovalDate: input.expectedApprovalDate + ? BigInt(input.expectedApprovalDate.getTime()) + : undefined, + createdAt: BigInt(input.createdAt.getTime()), + updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, + firstActivationAt: input.firstActivationAt + ? BigInt(input.firstActivationAt.getTime()) + : undefined, + suspendedAt: input.suspendedAt + ? BigInt(input.suspendedAt.getTime()) + : undefined, + riskAnalysis: input.riskAnalysis + ? toPurposeVersionDocumentV2(input.riskAnalysis) + : undefined, +}); + +export const toPurposeV2 = (input: Purpose): PurposeV2 => ({ + ...input, + versions: input.versions.map(toPurposeVersionV2), + createdAt: BigInt(input.createdAt.getTime()), + updatedAt: BigInt(input.updatedAt.getTime()), +}); From bdb35b4550d2aa6ce253d9590142ce78cd1396b4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 09:34:48 +0200 Subject: [PATCH 033/537] Add kafka topic --- packages/commons/src/config/kafkaTopicConfig.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/commons/src/config/kafkaTopicConfig.ts b/packages/commons/src/config/kafkaTopicConfig.ts index a4b5e17d09..1a7a42cc2a 100644 --- a/packages/commons/src/config/kafkaTopicConfig.ts +++ b/packages/commons/src/config/kafkaTopicConfig.ts @@ -44,10 +44,22 @@ export type AttributeTopicConfig = z.infer; export const attributeTopicConfig: () => AttributeTopicConfig = () => AttributeTopicConfig.parse(process.env); +export const PurposeTopicConfig = z + .object({ + PURPOSE_TOPIC: z.string(), + }) + .transform((c) => ({ + purposeTopic: c.PURPOSE_TOPIC, + })); +export type PurposeTopicConfig = z.infer; +export const purposeTopicConfig: () => PurposeTopicConfig = () => + PurposeTopicConfig.parse(process.env); + export const KafkaTopicConfig = z.union([ CatalogTopicConfig, AgreementTopicConfig, TenantTopicConfig, AttributeTopicConfig, + PurposeTopicConfig, ]); export type KafkaTopicConfig = z.infer; From c56260b2a47a5f621587eaa2255f79045386658c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 09:34:52 +0200 Subject: [PATCH 034/537] Add scaffold --- packages/purpose-readmodel-writer/.env | 17 + packages/purpose-readmodel-writer/.gitignore | 44 + packages/purpose-readmodel-writer/Dockerfile | 44 + packages/purpose-readmodel-writer/README.md | 11 + .../kubernetes/dev/configmap.yaml | 10 + .../kubernetes/dev/deployment.yaml | 90 + .../kubernetes/dev/serviceAccount.yaml | 9 + .../purpose-readmodel-writer/package.json | 44 + .../purpose-readmodel-writer/pnpm-lock.yaml | 2443 +++++++++++++++++ .../src/consumerServiceV1.ts | 8 + .../src/consumerServiceV2.ts | 8 + .../purpose-readmodel-writer/src/index.ts | 45 + .../test/consumerService.integration.test.ts | 46 + .../test/tsconfig.json | 4 + .../purpose-readmodel-writer/tsconfig.json | 9 + .../purpose-readmodel-writer/vitest.config.ts | 9 + pnpm-lock.yaml | 61 + 17 files changed, 2902 insertions(+) create mode 100644 packages/purpose-readmodel-writer/.env create mode 100644 packages/purpose-readmodel-writer/.gitignore create mode 100644 packages/purpose-readmodel-writer/Dockerfile create mode 100644 packages/purpose-readmodel-writer/README.md create mode 100644 packages/purpose-readmodel-writer/kubernetes/dev/configmap.yaml create mode 100644 packages/purpose-readmodel-writer/kubernetes/dev/deployment.yaml create mode 100644 packages/purpose-readmodel-writer/kubernetes/dev/serviceAccount.yaml create mode 100644 packages/purpose-readmodel-writer/package.json create mode 100644 packages/purpose-readmodel-writer/pnpm-lock.yaml create mode 100644 packages/purpose-readmodel-writer/src/consumerServiceV1.ts create mode 100644 packages/purpose-readmodel-writer/src/consumerServiceV2.ts create mode 100644 packages/purpose-readmodel-writer/src/index.ts create mode 100644 packages/purpose-readmodel-writer/test/consumerService.integration.test.ts create mode 100644 packages/purpose-readmodel-writer/test/tsconfig.json create mode 100644 packages/purpose-readmodel-writer/tsconfig.json create mode 100644 packages/purpose-readmodel-writer/vitest.config.ts diff --git a/packages/purpose-readmodel-writer/.env b/packages/purpose-readmodel-writer/.env new file mode 100644 index 0000000000..48e8edc45d --- /dev/null +++ b/packages/purpose-readmodel-writer/.env @@ -0,0 +1,17 @@ +HOST=0.0.0.0 +PORT=3000 +LOG_LEVEL=info + +KAFKA_CLIENT_ID="purpose" +KAFKA_GROUP_ID="purpose-group" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +PURPOSE_TOPIC="event-store.purpose.events" +READMODEL_DB_HOST="localhost" +READMODEL_DB_NAME="readmodel" +READMODEL_DB_USERNAME="root" +READMODEL_DB_PASSWORD="example" +READMODEL_DB_PORT=27017 +AWS_REGION="eu-central-1" + +WELL_KNOWN_URLS="https://dev.interop.pagopa.it/.well-known/jwks.json" diff --git a/packages/purpose-readmodel-writer/.gitignore b/packages/purpose-readmodel-writer/.gitignore new file mode 100644 index 0000000000..dece5f575e --- /dev/null +++ b/packages/purpose-readmodel-writer/.gitignore @@ -0,0 +1,44 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=node,visualstudiocode + +### Node ### +# Logs +logs +*.log +.pnpm-debug.log* + +# Dependency directories +node_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Output of 'npm pack' +*.tgz + + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +dist diff --git a/packages/purpose-readmodel-writer/Dockerfile b/packages/purpose-readmodel-writer/Dockerfile new file mode 100644 index 0000000000..f1618614fe --- /dev/null +++ b/packages/purpose-readmodel-writer/Dockerfile @@ -0,0 +1,44 @@ +FROM node:18.17.1-slim as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ + +COPY ./packages/purpose-readmodel-writer/package.json /app/packages/purpose-readmodel-writer/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/purpose-readmodel-writer /app/packages/purpose-readmodel-writer +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/purpose-readmodel-writer/node_modules \ + package*.json packages/purpose-readmodel-writer/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/purpose-readmodel-writer/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:18.17.1-slim as final + +COPY --from=build /out /app + +WORKDIR /app/packages/purpose-readmodel-writer +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/purpose-readmodel-writer/README.md b/packages/purpose-readmodel-writer/README.md new file mode 100644 index 0000000000..f2c7b5dc44 --- /dev/null +++ b/packages/purpose-readmodel-writer/README.md @@ -0,0 +1,11 @@ +# pagopa-interop-be-event-consumer-poc + +Kafka consumer that consumes Debezium events and updates the read model + +Node version required >=node16 + +Run consumer + +``` +pnpm start +``` diff --git a/packages/purpose-readmodel-writer/kubernetes/dev/configmap.yaml b/packages/purpose-readmodel-writer/kubernetes/dev/configmap.yaml new file mode 100644 index 0000000000..3bac191ce5 --- /dev/null +++ b/packages/purpose-readmodel-writer/kubernetes/dev/configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: interop-be-purpose-readmodel-writer + namespace: dev-refactor +data: + KAFKA_CLIENT_ID: "dev-pagopa-interop-purpose" + KAFKA_GROUP_ID: "purpose-readmodel-writer" + KAFKA_BROKERS: "boot-yqksbq44.c3.kafka-serverless.eu-central-1.amazonaws.com:9098" + PURPOSE_TOPIC: "event-store.purpose.events" diff --git a/packages/purpose-readmodel-writer/kubernetes/dev/deployment.yaml b/packages/purpose-readmodel-writer/kubernetes/dev/deployment.yaml new file mode 100644 index 0000000000..29314f3ff9 --- /dev/null +++ b/packages/purpose-readmodel-writer/kubernetes/dev/deployment.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: interop-be-purpose-readmodel-writer + namespace: dev-refactor + labels: + app: interop-be-purpose-readmodel-writer +spec: + replicas: 1 + selector: + matchLabels: + app: interop-be-purpose-readmodel-writer + template: + metadata: + labels: + app: interop-be-purpose-readmodel-writer + spec: + serviceAccountName: interop-be-purpose-readmodel-writer + containers: + - name: interop-be-purpose-readmodel-writer + image: ghcr.io/pagopa/purpose-readmodel-writer@$IMAGE_DIGEST + imagePullPolicy: Always + ports: + - name: http + containerPort: 3000 + protocol: TCP + resources: + requests: + cpu: 1.0 + memory: 2Gi + limits: + cpu: 1.0 + memory: 2Gi + env: + - name: HOST + value: "0.0.0.0" + - name: PORT + value: "3001" + - name: LOG_LEVEL + value: info + - name: READMODEL_DB_HOST + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_HOST + - name: READMODEL_DB_NAME + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_NAME + - name: READMODEL_DB_PORT + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_PORT + - name: WELL_KNOWN_URLS + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: WELL_KNOWN_URLS + - name: READMODEL_DB_USERNAME + valueFrom: + secretKeyRef: + name: read-model + key: REFACTOR_USERNAME + - name: READMODEL_DB_PASSWORD + valueFrom: + secretKeyRef: + name: read-model + key: REFACTOR_USER_PASSWORD + - name: KAFKA_CLIENT_ID + valueFrom: + configMapKeyRef: + name: interop-be-purpose-readmodel-writer + key: KAFKA_CLIENT_ID + - name: KAFKA_GROUP_ID + valueFrom: + configMapKeyRef: + name: interop-be-purpose-readmodel-writer + key: KAFKA_GROUP_ID + - name: KAFKA_BROKERS + valueFrom: + configMapKeyRef: + name: interop-be-purpose-readmodel-writer + key: KAFKA_BROKERS + - name: PURPOSE_TOPIC + valueFrom: + configMapKeyRef: + name: interop-be-purpose-readmodel-writer + key: PURPOSE_TOPIC diff --git a/packages/purpose-readmodel-writer/kubernetes/dev/serviceAccount.yaml b/packages/purpose-readmodel-writer/kubernetes/dev/serviceAccount.yaml new file mode 100644 index 0000000000..aff138d7d9 --- /dev/null +++ b/packages/purpose-readmodel-writer/kubernetes/dev/serviceAccount.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: interop-be-purpose-readmodel-writer + namespace: dev-refactor + labels: + app.kubernetes.io/name: interop-be-purpose-readmodel-writer + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::505630707203:role/interop-be-purpose-readmodel-writer-dev diff --git a/packages/purpose-readmodel-writer/package.json b/packages/purpose-readmodel-writer/package.json new file mode 100644 index 0000000000..5c135c218b --- /dev/null +++ b/packages/purpose-readmodel-writer/package.json @@ -0,0 +1,44 @@ +{ + "name": "pagopa-interop-purpose-readmodel-writer", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability purpose consumer service that updates the read model when events are stored", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "test:it": "vitest integration", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "node --watch --no-warnings --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/dotenv-flow": "3.2.0", + "@types/node": "20.3.1", + "@types/uuid": "9.0.2", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "testcontainers": "10.8.1", + "ts-node": "10.9.2", + "typescript": "5.1.3", + "vitest": "0.33.0" + }, + "dependencies": { + "@protobuf-ts/runtime": "2.9.4", + "dotenv-flow": "3.2.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.0.1", + "uuid": "9.0.0", + "zod": "3.21.4" + } +} diff --git a/packages/purpose-readmodel-writer/pnpm-lock.yaml b/packages/purpose-readmodel-writer/pnpm-lock.yaml new file mode 100644 index 0000000000..9337f55244 --- /dev/null +++ b/packages/purpose-readmodel-writer/pnpm-lock.yaml @@ -0,0 +1,2443 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + connection-string: + specifier: ^4.3.6 + version: 4.3.6 + dotenv-flow: + specifier: ^3.2.0 + version: 3.2.0 + kafkajs: + specifier: ^2.2.4 + version: 2.2.4 + mongodb: + specifier: 5.6.0 + version: 5.6.0 + nodemon: + specifier: ^2.0.22 + version: 2.0.22 + ts-pattern: + specifier: ^5.0.1 + version: 5.0.1 + winston: + specifier: ^3.9.0 + version: 3.9.0 + zod: + specifier: ^3.21.4 + version: 3.21.4 + +devDependencies: + '@pagopa/eslint-config': + specifier: ^3.0.0 + version: 3.0.0(typescript@5.1.3) + '@types/dotenv-flow': + specifier: ^3.2.0 + version: 3.2.0 + '@types/node': + specifier: ^20.3.1 + version: 20.3.1 + prettier: + specifier: ^2.8.8 + version: 2.8.8 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.3.1)(typescript@5.1.3) + typescript: + specifier: ^5.1.3 + version: 5.1.3 + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + dev: false + + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + + /@dabh/diagnostics@2.0.3: + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false + + /@es-joy/jsdoccomment@0.36.1: + resolution: {integrity: sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + dependencies: + comment-parser: 1.3.1 + esquery: 1.5.0 + jsdoc-type-pratt-parser: 3.1.0 + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.44.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.44.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /@eslint-community/regexpp@4.5.1: + resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.0: + resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.0 + globals: 13.20.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.44.0: + resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@humanwhocodes/config-array@0.11.10: + resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@pagopa/eslint-config@3.0.0(typescript@5.1.3): + resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + dependencies: + '@typescript-eslint/eslint-plugin': 5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + eslint: 8.44.0 + eslint-config-prettier: 8.8.0(eslint@8.44.0) + eslint-plugin-extra-rules: 0.0.0-development + eslint-plugin-fp: 2.3.0(eslint@8.44.0) + eslint-plugin-functional: 4.4.1(eslint@8.44.0)(typescript@5.1.3) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.61.0)(eslint@8.44.0) + eslint-plugin-jsdoc: 39.9.1(eslint@8.44.0) + eslint-plugin-prefer-arrow: 1.2.3(eslint@8.44.0) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.44.0)(prettier@2.8.8) + eslint-plugin-react: 7.32.2(eslint@8.44.0) + eslint-plugin-sonarjs: 0.13.0(eslint@8.44.0) + prettier: 2.8.8 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + - tsutils + - typescript + dev: true + + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + + /@types/dotenv-flow@3.2.0: + resolution: {integrity: sha512-A79hbPwocbYkcTwGcDOFbKDuqyVo5mLAz/6Iq465YZ7R7Go5bT1PIM8I2jlPQkaD9u9fbotGVLkUPhX+9XUHfw==} + dev: true + + /@types/json-schema@7.0.12: + resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + dev: true + + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + + /@types/node@20.3.1: + resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==} + + /@types/semver@7.5.0: + resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} + dev: true + + /@types/triple-beam@1.3.2: + resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} + dev: false + + /@types/webidl-conversions@7.0.0: + resolution: {integrity: sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==} + dev: false + + /@types/whatwg-url@8.2.2: + resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} + dependencies: + '@types/node': 20.3.1 + '@types/webidl-conversions': 7.0.0 + dev: false + + /@typescript-eslint/eslint-plugin@5.61.0(@typescript-eslint/parser@5.61.0)(eslint@8.44.0)(typescript@5.1.3): + resolution: {integrity: sha512-A5l/eUAug103qtkwccSCxn8ZRwT+7RXWkFECdA4Cvl1dOlDUgTpAOfSEElZn2uSUxhdDpnCdetrf0jvU4qrL+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.5.1 + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/scope-manager': 5.61.0 + '@typescript-eslint/type-utils': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + debug: 4.3.4 + eslint: 8.44.0 + graphemer: 1.4.0 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.3 + tsutils: 3.21.0(typescript@5.1.3) + typescript: 5.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.61.0(eslint@8.44.0)(typescript@5.1.3): + resolution: {integrity: sha512-yGr4Sgyh8uO6fSi9hw3jAFXNBHbCtKKFMdX2IkT3ZqpKmtAq3lHS4ixB/COFuAIJpwl9/AqF7j72ZDWYKmIfvg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.61.0 + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.3) + debug: 4.3.4 + eslint: 8.44.0 + typescript: 5.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.61.0: + resolution: {integrity: sha512-W8VoMjoSg7f7nqAROEmTt6LoBpn81AegP7uKhhW5KzYlehs8VV0ZW0fIDVbcZRcaP3aPSW+JZFua+ysQN+m/Nw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/visitor-keys': 5.61.0 + dev: true + + /@typescript-eslint/type-utils@5.61.0(eslint@8.44.0)(typescript@5.1.3): + resolution: {integrity: sha512-kk8u//r+oVK2Aj3ph/26XdH0pbAkC2RiSjUYhKD+PExemG4XSjpGFeyZ/QM8lBOa7O8aGOU+/yEbMJgQv/DnCg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.3) + '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + debug: 4.3.4 + eslint: 8.44.0 + tsutils: 3.21.0(typescript@5.1.3) + typescript: 5.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.61.0: + resolution: {integrity: sha512-ldyueo58KjngXpzloHUog/h9REmHl59G1b3a5Sng1GfBo14BkS3ZbMEb3693gnP1k//97lh7bKsp6/V/0v1veQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.61.0(typescript@5.1.3): + resolution: {integrity: sha512-Fud90PxONnnLZ36oR5ClJBLTLfU4pIWBmnvGwTbEa2cXIqj70AEDEmOmpkFComjBZ/037ueKrOdHuYmSFVD7Rw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/visitor-keys': 5.61.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.3 + tsutils: 3.21.0(typescript@5.1.3) + typescript: 5.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.61.0(eslint@8.44.0)(typescript@5.1.3): + resolution: {integrity: sha512-mV6O+6VgQmVE6+xzlA91xifndPW9ElFW8vbSF0xCT/czPXVhwDewKila1jOyRwa9AE19zKnrr7Cg5S3pJVrTWQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.0 + '@typescript-eslint/scope-manager': 5.61.0 + '@typescript-eslint/types': 5.61.0 + '@typescript-eslint/typescript-estree': 5.61.0(typescript@5.1.3) + eslint: 8.44.0 + eslint-scope: 5.1.1 + semver: 7.5.3 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.61.0: + resolution: {integrity: sha512-50XQ5VdbWrX06mQXhy93WywSFZZGsv3EOjq+lqp6WC2t+j3mb6A9xYVdrRxafvK88vg9k9u+CT4l6D8PEatjKg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.61.0 + eslint-visitor-keys: 3.4.1 + dev: true + + /abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: false + + /acorn-jsx@2.0.1: + resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + dependencies: + acorn: 2.7.0 + dev: true + + /acorn-jsx@5.3.2(acorn@8.9.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.9.0 + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@2.7.0: + resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn@8.9.0: + resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: false + + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array-includes@3.1.6: + resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + get-intrinsic: 1.2.1 + is-string: 1.0.7 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.flat@1.3.1: + resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.flatmap@1.3.1: + resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + dev: true + + /array.prototype.tosorted@1.1.1: + resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-shim-unscopables: 1.0.0 + get-intrinsic: 1.2.1 + dev: true + + /async@3.2.4: + resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} + dev: false + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + + /bson@5.3.0: + resolution: {integrity: sha512-ukmCZMneMlaC5ebPHXIkP8YJzNl5DC41N5MAIvKDqLggdao342t4McltoJBQfQya/nHBWAcSsYRqlXPoQkTJag==} + engines: {node: '>=14.20.1'} + dev: false + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: false + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: false + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: false + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: false + + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + dev: false + + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + dev: false + + /comment-parser@1.3.1: + resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} + engines: {node: '>= 12.0.0'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + /connection-string@4.3.6: + resolution: {integrity: sha512-dwaq4BMeiIIWry/oQxjstPB7Xp0K1nGjaY8p6PHQB+J9ZJuIvNp7ux3Noupq0hMd/dqEHXkfdmmGFOKTbGKWmw==} + engines: {node: '>=12', npm: '>=6'} + dev: false + + /console-assert@1.0.0: + resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} + dev: true + + /create-eslint-index@1.0.0: + resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} + engines: {node: '>=4.0.0'} + dependencies: + lodash.get: 4.4.2 + dev: true + + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@3.2.7(supports-color@5.5.0): + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + supports-color: 5.5.0 + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge-ts@4.3.0: + resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} + engines: {node: '>=12.4.0'} + dev: true + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dotenv-flow@3.2.0: + resolution: {integrity: sha512-GEB6RrR4AbqDJvNSFrYHqZ33IKKbzkvLYiD5eo4+9aFXr4Y4G+QaFrB/fNp0y6McWBmvaPn3ZNjIufnj8irCtg==} + engines: {node: '>= 8.0.0'} + dependencies: + dotenv: 8.6.0 + dev: false + + /dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + dev: false + + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false + + /es-abstract@1.21.2: + resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.1 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.10 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.5.0 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + dependencies: + has: 1.0.3 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-ast-utils@1.1.0: + resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} + engines: {node: '>=4'} + dependencies: + lodash.get: 4.4.2 + lodash.zip: 4.2.0 + dev: true + + /eslint-config-prettier@8.8.0(eslint@8.44.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.44.0 + dev: true + + /eslint-import-resolver-node@0.3.7: + resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} + dependencies: + debug: 3.2.7(supports-color@5.5.0) + is-core-module: 2.12.1 + resolve: 1.22.2 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + debug: 3.2.7(supports-color@5.5.0) + eslint: 8.44.0 + eslint-import-resolver-node: 0.3.7 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-extra-rules@0.0.0-development: + resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} + engines: {node: '> 0.10.*'} + dependencies: + console-assert: 1.0.0 + espree: 3.0.0-alpha-1 + quote: 0.4.0 + dev: true + + /eslint-plugin-fp@2.3.0(eslint@8.44.0): + resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} + engines: {node: '>=4.0.0'} + peerDependencies: + eslint: '>=3' + dependencies: + create-eslint-index: 1.0.0 + eslint: 8.44.0 + eslint-ast-utils: 1.1.0 + lodash: 4.17.21 + req-all: 0.1.0 + dev: true + + /eslint-plugin-functional@4.4.1(eslint@8.44.0)(typescript@5.1.3): + resolution: {integrity: sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^8.0.0 + tsutils: ^3.0.0 + typescript: ^3.4.1 || ^4.0.0 + peerDependenciesMeta: + tsutils: + optional: true + typescript: + optional: true + dependencies: + '@typescript-eslint/utils': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + deepmerge-ts: 4.3.0 + escape-string-regexp: 4.0.0 + eslint: 8.44.0 + semver: 7.5.3 + typescript: 5.1.3 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.61.0)(eslint@8.44.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.61.0(eslint@8.44.0)(typescript@5.1.3) + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7(supports-color@5.5.0) + doctrine: 2.1.0 + eslint: 8.44.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.61.0)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0) + has: 1.0.3 + is-core-module: 2.12.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.2 + semver: 6.3.0 + tsconfig-paths: 3.14.2 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-jsdoc@39.9.1(eslint@8.44.0): + resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + '@es-joy/jsdoccomment': 0.36.1 + comment-parser: 1.3.1 + debug: 4.3.4 + escape-string-regexp: 4.0.0 + eslint: 8.44.0 + esquery: 1.5.0 + semver: 7.5.3 + spdx-expression-parse: 3.0.1 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-prefer-arrow@1.2.3(eslint@8.44.0): + resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} + peerDependencies: + eslint: '>=2.0.0' + dependencies: + eslint: 8.44.0 + dev: true + + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0)(eslint@8.44.0)(prettier@2.8.8): + resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: '>=7.28.0' + eslint-config-prettier: '*' + prettier: '>=2.0.0' + peerDependenciesMeta: + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.44.0 + eslint-config-prettier: 8.8.0(eslint@8.44.0) + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + dev: true + + /eslint-plugin-react@7.32.2(eslint@8.44.0): + resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.6 + array.prototype.flatmap: 1.3.1 + array.prototype.tosorted: 1.1.1 + doctrine: 2.1.0 + eslint: 8.44.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.4 + minimatch: 3.1.2 + object.entries: 1.1.6 + object.fromentries: 2.0.6 + object.hasown: 1.1.2 + object.values: 1.1.6 + prop-types: 15.8.1 + resolve: 2.0.0-next.4 + semver: 6.3.0 + string.prototype.matchall: 4.0.8 + dev: true + + /eslint-plugin-sonarjs@0.13.0(eslint@8.44.0): + resolution: {integrity: sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==} + engines: {node: '>=12'} + peerDependencies: + eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.44.0 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.0: + resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.1: + resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.44.0: + resolution: {integrity: sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@eslint-community/regexpp': 4.5.1 + '@eslint/eslintrc': 2.1.0 + '@eslint/js': 8.44.0 + '@humanwhocodes/config-array': 0.11.10 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.0 + eslint-visitor-keys: 3.4.1 + espree: 9.6.0 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.20.0 + graphemer: 1.4.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@3.0.0-alpha-1: + resolution: {integrity: sha512-HIv6P6qCt3ciLWri1KrO7EPigKPetBZwfCf0o9TuAxRBEPoUUisCepsZqvM76xRfQf2sheO4BC5R/w3UKhwx4w==} + engines: {node: '>=0.10.0'} + hasBin: true + dependencies: + acorn: 2.7.0 + acorn-jsx: 2.0.1 + dev: true + + /espree@9.6.0: + resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.9.0 + acorn-jsx: 5.3.2(acorn@8.9.0) + eslint-visitor-keys: 3.4.1 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + + /fast-glob@3.3.0: + resolution: {integrity: sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name@1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + functions-have-names: 1.2.3 + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-proto: 1.0.1 + has-symbols: 1.0.3 + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@13.20.0: + resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.0 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.1 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + dev: false + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.1 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /ip@2.0.0: + resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + dev: false + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-typed-array: 1.1.10 + dev: true + + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: false + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.12.1: + resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsdoc-type-pratt-parser@3.1.0: + resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} + engines: {node: '>=12.0.0'} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /jsx-ast-utils@3.3.4: + resolution: {integrity: sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + object.assign: 4.1.4 + object.values: 1.1.6 + dev: true + + /kafkajs@2.2.4: + resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} + engines: {node: '>=14.0.0'} + dev: false + + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.zip@4.2.0: + resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /logform@2.5.1: + resolution: {integrity: sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg==} + dependencies: + '@colors/colors': 1.5.0 + '@types/triple-beam': 1.3.2 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.4.3 + triple-beam: 1.3.0 + dev: false + + /loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + dependencies: + js-tokens: 4.0.0 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + dev: false + optional: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mongodb-connection-string-url@2.6.0: + resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} + dependencies: + '@types/whatwg-url': 8.2.2 + whatwg-url: 11.0.0 + dev: false + + /mongodb@5.6.0: + resolution: {integrity: sha512-z8qVs9NfobHJm6uzK56XBZF8XwM9H294iRnB7wNjF0SnY93si5HPziIJn+qqvUR5QOff/4L0gCD6SShdR/GtVQ==} + engines: {node: '>=14.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.201.0 + mongodb-client-encryption: '>=2.3.0 <3' + snappy: ^7.2.2 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + dependencies: + bson: 5.3.0 + mongodb-connection-string-url: 2.6.0 + socks: 2.7.1 + optionalDependencies: + saslprep: 1.0.3 + dev: false + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /nodemon@2.0.22: + resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} + engines: {node: '>=8.10.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + debug: 3.2.7(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 5.7.1 + simple-update-notifier: 1.1.0 + supports-color: 5.5.0 + touch: 3.1.0 + undefsafe: 2.0.5 + dev: false + + /nopt@1.0.10: + resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: false + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: false + + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: true + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.entries@1.1.6: + resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /object.fromentries@2.0.6: + resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /object.hasown@1.1.2: + resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} + dependencies: + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /object.values@1.1.6: + resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + dependencies: + fn.name: 1.1.0 + dev: false + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + + /pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + dev: false + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quote@0.4.0: + resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} + dev: true + + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + dev: false + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: false + + /regexp.prototype.flags@1.5.0: + resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /req-all@0.1.0: + resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} + engines: {node: '>=4'} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve@1.22.2: + resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} + hasBin: true + dependencies: + is-core-module: 2.12.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /resolve@2.0.0-next.4: + resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + hasBin: true + dependencies: + is-core-module: 2.12.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + is-regex: 1.1.4 + dev: true + + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + dev: false + + /saslprep@1.0.3: + resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + sparse-bitfield: 3.0.3 + dev: false + optional: true + + /semver@5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: false + + /semver@6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver@7.0.0: + resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} + hasBin: true + dev: false + + /semver@7.5.3: + resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.1 + object-inspect: 1.12.3 + dev: true + + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: false + + /simple-update-notifier@1.1.0: + resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} + engines: {node: '>=8.10.0'} + dependencies: + semver: 7.0.0 + dev: false + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: false + + /socks@2.7.1: + resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} + engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + dependencies: + ip: 2.0.0 + smart-buffer: 4.2.0 + dev: false + + /sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + dependencies: + memory-pager: 1.5.0 + dev: false + optional: true + + /spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + dev: true + + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + dependencies: + spdx-exceptions: 2.3.0 + spdx-license-ids: 3.0.13 + dev: true + + /spdx-license-ids@3.0.13: + resolution: {integrity: sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==} + dev: true + + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: false + + /string.prototype.matchall@4.0.8: + resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + get-intrinsic: 1.2.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + regexp.prototype.flags: 1.5.0 + side-channel: 1.0.4 + dev: true + + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + + /touch@3.1.0: + resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} + hasBin: true + dependencies: + nopt: 1.0.10 + dev: false + + /tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.0 + dev: false + + /triple-beam@1.3.0: + resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} + dev: false + + /ts-node@10.9.1(@types/node@20.3.1)(typescript@5.1.3): + resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.3.1 + acorn: 8.9.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.1.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ts-pattern@5.0.1: + resolution: {integrity: sha512-ZyNm28Lsg34Co5DS3e9DVyjlX2Y+2exkI4jqTKyU+9/OL6Y2fKOOuL8i+7no71o74C6mVS+UFoP3ekM3iCT1HQ==} + dev: false + + /tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tsutils@3.21.0(typescript@5.1.3): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.1.3 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + + /typescript@5.1.3: + resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + dev: false + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false + + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: false + + /whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: false + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-typed-array@1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /winston-transport@4.5.0: + resolution: {integrity: sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==} + engines: {node: '>= 6.4.0'} + dependencies: + logform: 2.5.1 + readable-stream: 3.6.2 + triple-beam: 1.3.0 + dev: false + + /winston@3.9.0: + resolution: {integrity: sha512-jW51iW/X95BCW6MMtZWr2jKQBP4hV5bIDq9QrIjfDk6Q9QuxvTKEAlpUNAzP+HYHFFCeENhph16s0zEunu4uuQ==} + engines: {node: '>= 12.0.0'} + dependencies: + '@colors/colors': 1.5.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.4 + is-stream: 2.0.1 + logform: 2.5.1 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.4.3 + stack-trace: 0.0.10 + triple-beam: 1.3.0 + winston-transport: 4.5.0 + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + /zod@3.21.4: + resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + dev: false diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV1.ts b/packages/purpose-readmodel-writer/src/consumerServiceV1.ts new file mode 100644 index 0000000000..d93a0b9834 --- /dev/null +++ b/packages/purpose-readmodel-writer/src/consumerServiceV1.ts @@ -0,0 +1,8 @@ +import { PurposeCollection } from "pagopa-interop-commons"; +import { PurposeEventEnvelopeV1 } from "pagopa-interop-models"; + +export async function handleMessageV1( + _message: PurposeEventEnvelopeV1, + _purposes: PurposeCollection + // eslint-disable-next-line @typescript-eslint/no-empty-function +): Promise {} diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV2.ts b/packages/purpose-readmodel-writer/src/consumerServiceV2.ts new file mode 100644 index 0000000000..88a1970f80 --- /dev/null +++ b/packages/purpose-readmodel-writer/src/consumerServiceV2.ts @@ -0,0 +1,8 @@ +import { PurposeCollection } from "pagopa-interop-commons"; +import { PurposeEventEnvelopeV2 } from "pagopa-interop-models"; + +export async function handleMessageV2( + _message: PurposeEventEnvelopeV2, + _purposes: PurposeCollection + // eslint-disable-next-line @typescript-eslint/no-empty-function +): Promise {} diff --git a/packages/purpose-readmodel-writer/src/index.ts b/packages/purpose-readmodel-writer/src/index.ts new file mode 100644 index 0000000000..a53d27f1d0 --- /dev/null +++ b/packages/purpose-readmodel-writer/src/index.ts @@ -0,0 +1,45 @@ +/* eslint-disable functional/immutable-data */ +import { EachMessagePayload } from "kafkajs"; +import { + logger, + ReadModelRepository, + readModelWriterConfig, + decodeKafkaMessage, + getContext, + purposeTopicConfig, +} from "pagopa-interop-commons"; +import { v4 } from "uuid"; +import { runConsumer } from "kafka-iam-auth"; +import { PurposeEvent } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { handleMessageV1 } from "./consumerServiceV1.js"; +import { handleMessageV2 } from "./consumerServiceV2.js"; + +const config = readModelWriterConfig(); +const { purposeTopic } = purposeTopicConfig(); +const { purposes } = ReadModelRepository.init(config); + +async function processMessage({ + message, + partition, +}: EachMessagePayload): Promise { + const decodedMessage = decodeKafkaMessage(message, PurposeEvent); + const ctx = getContext(); + ctx.messageData = { + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + }; + ctx.correlationId = decodedMessage.correlation_id || v4(); + + await match(decodedMessage) + .with({ event_version: 1 }, (msg) => handleMessageV1(msg, purposes)) + .with({ event_version: 2 }, (msg) => handleMessageV2(msg, purposes)) + .exhaustive(); + + logger.info( + `Read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + ); +} + +await runConsumer(config, [purposeTopic], processMessage); diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts new file mode 100644 index 0000000000..ce8391bed7 --- /dev/null +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -0,0 +1,46 @@ +/* eslint-disable functional/immutable-data */ +/* eslint-disable functional/no-let */ +import { afterEach, afterAll, beforeAll, describe, expect, it } from "vitest"; +import { + PurposeCollection, + ReadModelRepository, + readModelWriterConfig, +} from "pagopa-interop-commons"; +import { mongoDBContainer } from "pagopa-interop-commons-test"; +import { StartedTestContainer } from "testcontainers"; + +describe("Integration tests", async () => { + let purposes: PurposeCollection; + let startedMongoDBContainer: StartedTestContainer; + + const config = readModelWriterConfig(); + + beforeAll(async () => { + startedMongoDBContainer = await mongoDBContainer(config).start(); + + config.readModelDbPort = startedMongoDBContainer.getMappedPort(27017); + + const readModelRepository = ReadModelRepository.init(config); + purposes = readModelRepository.purposes; + }); + + afterEach(async () => { + await purposes.deleteMany({}); + }); + + afterAll(async () => { + await startedMongoDBContainer.stop(); + }); + + describe("Events V1", () => { + it("PurposeAdded", () => { + expect(1).toBe(1); + }); + }); + + describe("Events V2", () => { + it("PurposeAdded", () => { + expect(2).toBe(2); + }); + }); +}); diff --git a/packages/purpose-readmodel-writer/test/tsconfig.json b/packages/purpose-readmodel-writer/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/purpose-readmodel-writer/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/purpose-readmodel-writer/tsconfig.json b/packages/purpose-readmodel-writer/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/purpose-readmodel-writer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/purpose-readmodel-writer/vitest.config.ts b/packages/purpose-readmodel-writer/vitest.config.ts new file mode 100644 index 0000000000..498a780952 --- /dev/null +++ b/packages/purpose-readmodel-writer/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + setupFiles: ["dotenv/config"], + testTimeout: 60000, + hookTimeout: 60000, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d3d6641f5..6492df8f68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -940,6 +940,67 @@ importers: specifier: 0.33.0 version: 0.33.0 + packages/purpose-readmodel-writer: + dependencies: + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + dotenv-flow: + specifier: 3.2.0 + version: 3.2.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + kafkajs: + specifier: 2.2.4 + version: 2.2.4 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.0.1 + version: 5.0.1 + uuid: + specifier: 9.0.0 + version: 9.0.0 + zod: + specifier: 3.21.4 + version: 3.21.4 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(typescript@5.1.3) + '@types/dotenv-flow': + specifier: 3.2.0 + version: 3.2.0 + '@types/node': + specifier: 20.3.1 + version: 20.3.1 + '@types/uuid': + specifier: 9.0.2 + version: 9.0.2 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.8.1 + version: 10.8.1 + ts-node: + specifier: 10.9.2 + version: 10.9.2(@types/node@20.3.1)(typescript@5.1.3) + typescript: + specifier: 5.1.3 + version: 5.1.3 + vitest: + specifier: 0.33.0 + version: 0.33.0 + packages/tenant-process: dependencies: '@types/uuid': From 875c0868506aed9b8aa9648d977449f81fcf4437 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 09:38:22 +0200 Subject: [PATCH 035/537] Minor change in brandedIds --- packages/models/src/brandedIds.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 8a03daf41e..2506c2d96f 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -44,6 +44,9 @@ export type RiskAnalysisMultiAnswerId = z.infer< export const RiskAnalysisFormId = z.string().uuid().brand("RiskAnalysisFormId"); export type RiskAnalysisFormId = z.infer; +export const RiskAnalysisId = z.string().uuid().brand("RiskAnalysisId"); +export type RiskAnalysisId = z.infer; + export const PurposeRiskAnalysisFormId = z .string() .uuid() @@ -52,9 +55,6 @@ export type PurposeRiskAnalysisFormId = z.infer< typeof PurposeRiskAnalysisFormId >; -export const RiskAnalysisId = z.string().uuid().brand("RiskAnalysisId"); -export type RiskAnalysisId = z.infer; - export const PurposeId = z.string().uuid().brand("PurposeId"); export type PurposeId = z.infer; From 15684bfc581b9f500fb400c2f3fd9210f27db50b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 09:40:37 +0200 Subject: [PATCH 036/537] Fix typos --- packages/models/proto/v2/purpose/events.proto | 8 ++-- packages/models/src/purpose/purposeEvents.ts | 40 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto index 5471f5da50..3e7a919296 100644 --- a/packages/models/proto/v2/purpose/events.proto +++ b/packages/models/proto/v2/purpose/events.proto @@ -37,22 +37,22 @@ message PurposeVersionActivatedV2 { PurposeV2 purpose = 1; } -message PurposeVersionUnsuspenedByProducerV2 { +message PurposeVersionUnsuspendedByProducerV2 { string versionId = 1; PurposeV2 purpose = 2; } -message PurposeVersionUnsuspenedByConsumerV2 { +message PurposeVersionUnsuspendedByConsumerV2 { string versionId = 1; PurposeV2 purpose = 2; } -message PurposeVersionSuspenedByProducerV2 { +message PurposeVersionSuspendedByProducerV2 { string versionId = 1; PurposeV2 purpose = 2; } -message PurposeVersionSuspenedByConsumerV2 { +message PurposeVersionSuspendedByConsumerV2 { string versionId = 1; PurposeV2 purpose = 2; } diff --git a/packages/models/src/purpose/purposeEvents.ts b/packages/models/src/purpose/purposeEvents.ts index 9c057f06c5..d7778608c8 100644 --- a/packages/models/src/purpose/purposeEvents.ts +++ b/packages/models/src/purpose/purposeEvents.ts @@ -24,10 +24,10 @@ import { WaitingForApprovalPurposeDeletedV2, NewPurposeVersionActivatedV2, PurposeVersionActivatedV2, - PurposeVersionUnsuspenedByProducerV2, - PurposeVersionUnsuspenedByConsumerV2, - PurposeVersionSuspenedByProducerV2, - PurposeVersionSuspenedByConsumerV2, + PurposeVersionUnsuspendedByProducerV2, + PurposeVersionUnsuspendedByConsumerV2, + PurposeVersionSuspendedByProducerV2, + PurposeVersionSuspendedByConsumerV2, NewPurposeVersionWaitingForApprovalV2, PurposeVersionOverQuotaUnsuspendedV2, PurposeArchivedV2, @@ -104,17 +104,17 @@ export function purposeEventToBinaryDataV2(event: PurposeEventV2): Uint8Array { .with({ type: "PurposeVersionActivated" }, ({ data }) => PurposeVersionActivatedV2.toBinary(data) ) - .with({ type: "PurposeVersionUnsuspenedByProducer" }, ({ data }) => - PurposeVersionUnsuspenedByProducerV2.toBinary(data) + .with({ type: "PurposeVersionUnsuspendedByProducer" }, ({ data }) => + PurposeVersionUnsuspendedByProducerV2.toBinary(data) ) - .with({ type: "PurposeVersionUnsuspenedByConsumer" }, ({ data }) => - PurposeVersionUnsuspenedByConsumerV2.toBinary(data) + .with({ type: "PurposeVersionUnsuspendedByConsumer" }, ({ data }) => + PurposeVersionUnsuspendedByConsumerV2.toBinary(data) ) - .with({ type: "PurposeVersionSuspenedByProducer" }, ({ data }) => - PurposeVersionSuspenedByProducerV2.toBinary(data) + .with({ type: "PurposeVersionSuspendedByProducer" }, ({ data }) => + PurposeVersionSuspendedByProducerV2.toBinary(data) ) - .with({ type: "PurposeVersionSuspenedByConsumer" }, ({ data }) => - PurposeVersionSuspenedByConsumerV2.toBinary(data) + .with({ type: "PurposeVersionSuspendedByConsumer" }, ({ data }) => + PurposeVersionSuspendedByConsumerV2.toBinary(data) ) .with({ type: "NewPurposeVersionWaitingForApproval" }, ({ data }) => NewPurposeVersionWaitingForApprovalV2.toBinary(data) @@ -236,23 +236,23 @@ export const PurposeEventV2 = z.discriminatedUnion("type", [ }), z.object({ event_version: z.literal(2), - type: z.literal("PurposeVersionUnsuspenedByProducer"), - data: protobufDecoder(PurposeVersionUnsuspenedByProducerV2), + type: z.literal("PurposeVersionUnsuspendedByProducer"), + data: protobufDecoder(PurposeVersionUnsuspendedByProducerV2), }), z.object({ event_version: z.literal(2), - type: z.literal("PurposeVersionUnsuspenedByConsumer"), - data: protobufDecoder(PurposeVersionUnsuspenedByConsumerV2), + type: z.literal("PurposeVersionUnsuspendedByConsumer"), + data: protobufDecoder(PurposeVersionUnsuspendedByConsumerV2), }), z.object({ event_version: z.literal(2), - type: z.literal("PurposeVersionSuspenedByProducer"), - data: protobufDecoder(PurposeVersionSuspenedByProducerV2), + type: z.literal("PurposeVersionSuspendedByProducer"), + data: protobufDecoder(PurposeVersionSuspendedByProducerV2), }), z.object({ event_version: z.literal(2), - type: z.literal("PurposeVersionSuspenedByConsumer"), - data: protobufDecoder(PurposeVersionSuspenedByConsumerV2), + type: z.literal("PurposeVersionSuspendedByConsumer"), + data: protobufDecoder(PurposeVersionSuspendedByConsumerV2), }), z.object({ event_version: z.literal(2), From a125bda2b2fdbb619cc063b35a8c310087e91ea7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 09:42:11 +0200 Subject: [PATCH 037/537] Update comment --- packages/models/src/risk-analysis/riskAnalysis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index fe67475b7b..e28c5e7258 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -31,7 +31,7 @@ export type RiskAnalysisForm = z.infer; export const PurposeRiskAnalysisForm = z.object({ id: PurposeRiskAnalysisFormId, - riskAnalysisId: z.string().optional(), // TO DO: it should be RiskAnalysis.Id.optional() + riskAnalysisId: z.string().optional(), // TO DO: it should be RiskAnalysisId.optional() version: z.string(), singleAnswers: z.array(RiskAnalysisSingleAnswer), multiAnswers: z.array(RiskAnalysisMultiAnswer), From 3c71595a07400a99a1fea7a41a635855947f6f00 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:11:21 +0200 Subject: [PATCH 038/537] Fix --- packages/models/src/purpose/protobufConverterFromV1.ts | 4 ++-- packages/models/src/risk-analysis/riskAnalysis.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index ae8846cf0d..612b36370b 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -4,7 +4,7 @@ import { PurposeVersionDocumentV1, PurposeVersionV1, } from "../gen/v1/purpose/purpose.js"; -import { unsafeBrandId } from "../brandedIds.js"; +import { RiskAnalysisId, unsafeBrandId } from "../brandedIds.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; import { PurposeRiskAnalysisFormV1 } from "../gen/v1/purpose/riskAnalysis.js"; import { @@ -69,7 +69,7 @@ export const fromPurposeRiskAnalysisFormV1 = ( ...input, id: unsafeBrandId(input.id), riskAnalysisId: input.riskAnalysisId - ? unsafeBrandId(input.riskAnalysisId) + ? unsafeBrandId(input.riskAnalysisId) : undefined, singleAnswers: input.singleAnswers.map((a) => ({ ...a, diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index e28c5e7258..11e68f3692 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -31,7 +31,7 @@ export type RiskAnalysisForm = z.infer; export const PurposeRiskAnalysisForm = z.object({ id: PurposeRiskAnalysisFormId, - riskAnalysisId: z.string().optional(), // TO DO: it should be RiskAnalysisId.optional() + riskAnalysisId: RiskAnalysisId.optional(), version: z.string(), singleAnswers: z.array(RiskAnalysisSingleAnswer), multiAnswers: z.array(RiskAnalysisMultiAnswer), From 4a90b96782b9eeda9fc87e39bc7e4d394ba6900a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:15:15 +0200 Subject: [PATCH 039/537] Fix --- packages/models/src/purpose/protobufConverterFromV2.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index 64ab55ea57..bca21b29cd 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -1,4 +1,4 @@ -import { unsafeBrandId } from "../brandedIds.js"; +import { RiskAnalysisId, unsafeBrandId } from "../brandedIds.js"; import { PurposeStateV2, PurposeVersionDocumentV2, @@ -66,7 +66,7 @@ export const fromPurposeRiskAnalysisFormV2 = ( ...input, id: unsafeBrandId(input.id), riskAnalysisId: input.riskAnalysisId - ? unsafeBrandId(input.riskAnalysisId) + ? unsafeBrandId(input.riskAnalysisId) : undefined, singleAnswers: input.singleAnswers.map((a) => ({ ...a, From be2c15f75a62fa249286a57fa400f0c403ffdc7f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:38:48 +0200 Subject: [PATCH 040/537] Add implementation --- .../src/consumerServiceV1.ts | 121 +++++++++++++++++- 1 file changed, 116 insertions(+), 5 deletions(-) diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV1.ts b/packages/purpose-readmodel-writer/src/consumerServiceV1.ts index d93a0b9834..d015e289d3 100644 --- a/packages/purpose-readmodel-writer/src/consumerServiceV1.ts +++ b/packages/purpose-readmodel-writer/src/consumerServiceV1.ts @@ -1,8 +1,119 @@ import { PurposeCollection } from "pagopa-interop-commons"; -import { PurposeEventEnvelopeV1 } from "pagopa-interop-models"; +import { + PurposeEventEnvelopeV1, + fromPurposeV1, + fromPurposeVersionV1, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; export async function handleMessageV1( - _message: PurposeEventEnvelopeV1, - _purposes: PurposeCollection - // eslint-disable-next-line @typescript-eslint/no-empty-function -): Promise {} + message: PurposeEventEnvelopeV1, + purposes: PurposeCollection +): Promise { + match(message) + .with( + { type: "PurposeCreated" }, + async (msg) => + await purposes.updateOne( + { "data.id": msg.stream_id }, + { + $setOnInsert: { + data: msg.data.purpose + ? fromPurposeV1(msg.data.purpose) + : undefined, + metadata: { version: msg.version }, + }, + }, + { upsert: true } + ) + ) + .with( + { type: "PurposeVersionCreated" }, + async (msg) => + await purposes.updateOne( + { + "data.id": msg.stream_id, + "metadata.version": { $lt: msg.version }, + }, + { + $set: { + "metadata.version": msg.version, + }, + $push: { + "data.versions": msg.data.version + ? fromPurposeVersionV1(msg.data.version) + : undefined, + }, + } + ) + ) + .with( + { type: "PurposeUpdated" }, + { type: "PurposeVersionActivated" }, + { type: "PurposeVersionSuspended" }, + { type: "PurposeVersionArchived" }, + { type: "PurposeVersionWaitedForApproval" }, + { type: "PurposeVersionRejected" }, + async (msg) => + await purposes.updateOne( + { "data.id": msg.stream_id }, + { + $setOnInsert: { + data: msg.data.purpose + ? fromPurposeV1(msg.data.purpose) + : undefined, + metadata: { version: msg.version }, + }, + } + ) + ) + .with({ type: "PurposeVersionUpdated" }, async (msg) => { + await purposes.updateOne( + { + "data.id": msg.stream_id, + "metadata.version": { $lt: msg.version }, + }, + { + $set: { + "metadata.version": msg.version, + "data.versions.$[version]": msg.data.version + ? fromPurposeVersionV1(msg.data.version) + : undefined, + }, + }, + { + arrayFilters: [ + { + "version.id": msg.data.version?.id, + }, + ], + } + ); + }) + .with({ type: "PurposeDeleted" }, async (msg) => { + await purposes.deleteOne({ + "data.id": msg.stream_id, + "metadata.version": { $lt: msg.version }, + }); + }) + .with( + { type: "PurposeVersionDeleted" }, + async (msg) => + await purposes.updateOne( + { + "data.id": msg.stream_id, + "metadata.version": { $lt: msg.version }, + }, + { + $pull: { + "data.versions": { + id: msg.data.versionId, + }, + }, + $set: { + "metadata.version": msg.version, + }, + } + ) + ); +} From 1079eb6320db2f98c2dbcb1c661105493c0ac4b0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:40:26 +0200 Subject: [PATCH 041/537] Add tests scaffold --- .../test/consumerService.integration.test.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index ce8391bed7..10c8716295 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -33,7 +33,37 @@ describe("Integration tests", async () => { }); describe("Events V1", () => { - it("PurposeAdded", () => { + it("PurposeCreated", () => { + expect(1).toBe(1); + }); + it("PurposeVersionCreated", () => { + expect(1).toBe(1); + }); + it("PurposeUpdated", () => { + expect(1).toBe(1); + }); + it("PurposeVersionActivated", () => { + expect(1).toBe(1); + }); + it("PurposeVersionSuspended", () => { + expect(1).toBe(1); + }); + it("PurposeVersionArchived", () => { + expect(1).toBe(1); + }); + it("PurposeVersionWaitedForApproval", () => { + expect(1).toBe(1); + }); + it("PurposeVersionRejected", () => { + expect(1).toBe(1); + }); + it("PurposeVersionUpdated", () => { + expect(1).toBe(1); + }); + it("PurposeDeleted", () => { + expect(1).toBe(1); + }); + it("PurposeVersionDeleted", () => { expect(1).toBe(1); }); }); From 7db91d45a3815d6f9189c73f914d8d83a592ad4a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:43:37 +0200 Subject: [PATCH 042/537] Renaming --- .../src/{consumerServiceV1.ts => purposeConsumerServiceV1.ts} | 0 .../src/{consumerServiceV2.ts => purposeConsumerServiceV2.ts} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename packages/purpose-readmodel-writer/src/{consumerServiceV1.ts => purposeConsumerServiceV1.ts} (100%) rename packages/purpose-readmodel-writer/src/{consumerServiceV2.ts => purposeConsumerServiceV2.ts} (100%) diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV1.ts b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts similarity index 100% rename from packages/purpose-readmodel-writer/src/consumerServiceV1.ts rename to packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV2.ts b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV2.ts similarity index 100% rename from packages/purpose-readmodel-writer/src/consumerServiceV2.ts rename to packages/purpose-readmodel-writer/src/purposeConsumerServiceV2.ts From e6d825840b411cf9056f89a645d716ea87b22e59 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:44:23 +0200 Subject: [PATCH 043/537] Renaming --- packages/purpose-readmodel-writer/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-readmodel-writer/src/index.ts b/packages/purpose-readmodel-writer/src/index.ts index a53d27f1d0..d652cde75b 100644 --- a/packages/purpose-readmodel-writer/src/index.ts +++ b/packages/purpose-readmodel-writer/src/index.ts @@ -12,8 +12,8 @@ import { v4 } from "uuid"; import { runConsumer } from "kafka-iam-auth"; import { PurposeEvent } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { handleMessageV1 } from "./consumerServiceV1.js"; -import { handleMessageV2 } from "./consumerServiceV2.js"; +import { handleMessageV1 } from "./purposeConsumerServiceV1.js"; +import { handleMessageV2 } from "./purposeConsumerServiceV2.js"; const config = readModelWriterConfig(); const { purposeTopic } = purposeTopicConfig(); From a65375123d32bb415b2f616e8b178376c0259c3f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:44:56 +0200 Subject: [PATCH 044/537] Renaming --- packages/purpose-readmodel-writer/src/index.ts | 4 ++-- .../src/{consumerServiceV1.ts => purposeConsumerServiceV1.ts} | 0 .../src/{consumerServiceV2.ts => purposeConsumerServiceV2.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/purpose-readmodel-writer/src/{consumerServiceV1.ts => purposeConsumerServiceV1.ts} (100%) rename packages/purpose-readmodel-writer/src/{consumerServiceV2.ts => purposeConsumerServiceV2.ts} (100%) diff --git a/packages/purpose-readmodel-writer/src/index.ts b/packages/purpose-readmodel-writer/src/index.ts index a53d27f1d0..d652cde75b 100644 --- a/packages/purpose-readmodel-writer/src/index.ts +++ b/packages/purpose-readmodel-writer/src/index.ts @@ -12,8 +12,8 @@ import { v4 } from "uuid"; import { runConsumer } from "kafka-iam-auth"; import { PurposeEvent } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { handleMessageV1 } from "./consumerServiceV1.js"; -import { handleMessageV2 } from "./consumerServiceV2.js"; +import { handleMessageV1 } from "./purposeConsumerServiceV1.js"; +import { handleMessageV2 } from "./purposeConsumerServiceV2.js"; const config = readModelWriterConfig(); const { purposeTopic } = purposeTopicConfig(); diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV1.ts b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts similarity index 100% rename from packages/purpose-readmodel-writer/src/consumerServiceV1.ts rename to packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts diff --git a/packages/purpose-readmodel-writer/src/consumerServiceV2.ts b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV2.ts similarity index 100% rename from packages/purpose-readmodel-writer/src/consumerServiceV2.ts rename to packages/purpose-readmodel-writer/src/purposeConsumerServiceV2.ts From a16af36b0890f47e76c13ccf98250c68dbdfb194 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:46:26 +0200 Subject: [PATCH 045/537] Add exhaustive check --- .../src/purposeConsumerServiceV1.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts index 8fe1dc98b5..f6f7f2bb47 100644 --- a/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts +++ b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts @@ -10,7 +10,7 @@ export async function handleMessageV1( message: PurposeEventEnvelopeV1, purposes: PurposeCollection ): Promise { - match(message) + await match(message) .with( { type: "PurposeCreated" }, async (msg) => @@ -115,5 +115,6 @@ export async function handleMessageV1( }, } ) - ); -} \ No newline at end of file + ) + .exhaustive(); +} From a9b1ff839b4f637b95dc0c675df1601bd878f234 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:54:29 +0200 Subject: [PATCH 046/537] Update purpose model --- packages/models/src/purpose/purpose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 5de0475456..d90a9153a4 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -55,7 +55,7 @@ export const Purpose = z.object({ description: z.string(), riskAnalysisForm: PurposeRiskAnalysisForm.optional(), createdAt: z.coerce.date(), - updatedAt: z.coerce.date(), + updatedAt: z.coerce.date().optional(), isFreeOfCharge: z.boolean(), freeOfChargeReason: z.string().optional(), }); From dc37d85a51bfd67f82106f05c5f7360be9c32801 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 10:58:17 +0200 Subject: [PATCH 047/537] Fix dates --- packages/models/src/purpose/protobufConverterFromV1.ts | 8 +++++--- packages/models/src/purpose/protobufConverterToV1.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index 612b36370b..82c6d21df6 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -58,9 +58,11 @@ export const fromPurposeVersionV1 = ( ? fromPurposeVersionDocumentV1(input.riskAnalysis) : undefined, createdAt: new Date(Number(input.createdAt)), - updatedAt: new Date(Number(input.updatedAt)), + updatedAt: input.updatedAt ? new Date(Number(input.updatedAt)) : undefined, firstActivationAt: new Date(Number(input.updatedAt)), - suspendedAt: new Date(Number(input.suspendedAt)), + suspendedAt: input.suspendedAt + ? new Date(Number(input.suspendedAt)) + : undefined, }); export const fromPurposeRiskAnalysisFormV1 = ( @@ -89,7 +91,7 @@ export const fromPurposeV1 = (input: PurposeV1): Purpose => ({ versions: input.versions.map(fromPurposeVersionV1), isFreeOfCharge: input.isFreeOfCharge || true, createdAt: new Date(Number(input.createdAt)), - updatedAt: new Date(Number(input.updatedAt)), + updatedAt: input.updatedAt ? new Date(Number(input.updatedAt)) : undefined, riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV1(input.riskAnalysisForm) : undefined, diff --git a/packages/models/src/purpose/protobufConverterToV1.ts b/packages/models/src/purpose/protobufConverterToV1.ts index ceb80a5803..c0b83fcb09 100644 --- a/packages/models/src/purpose/protobufConverterToV1.ts +++ b/packages/models/src/purpose/protobufConverterToV1.ts @@ -61,5 +61,5 @@ export const toPurposeV1 = (input: Purpose): PurposeV1 => ({ ...input, versions: input.versions.map(toPurposeVersionV1), createdAt: BigInt(input.createdAt.getTime()), - updatedAt: BigInt(input.updatedAt.getTime()), + updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, }); From 8877a7857007b9910d652cf6069cc710765f32c5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:01:54 +0200 Subject: [PATCH 048/537] Fix updatedAt --- packages/models/src/purpose/protobufConverterToV2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/protobufConverterToV2.ts b/packages/models/src/purpose/protobufConverterToV2.ts index 4045a9fb1e..d200eb2364 100644 --- a/packages/models/src/purpose/protobufConverterToV2.ts +++ b/packages/models/src/purpose/protobufConverterToV2.ts @@ -60,5 +60,5 @@ export const toPurposeV2 = (input: Purpose): PurposeV2 => ({ ...input, versions: input.versions.map(toPurposeVersionV2), createdAt: BigInt(input.createdAt.getTime()), - updatedAt: BigInt(input.updatedAt.getTime()), + updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, }); From aabb876c1dd2ea63417b3f1adc553d9520e7243e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:33:33 +0200 Subject: [PATCH 049/537] Implement test --- .../test/consumerService.integration.test.ts | 460 +++++++++++++++++- 1 file changed, 437 insertions(+), 23 deletions(-) diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index 10c8716295..67f8f28979 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -6,8 +6,32 @@ import { ReadModelRepository, readModelWriterConfig, } from "pagopa-interop-commons"; -import { mongoDBContainer } from "pagopa-interop-commons-test"; +import { + mongoDBContainer, + writeInReadmodel, +} from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; +import { + Purpose, + PurposeCreatedV1, + PurposeDeletedV1, + PurposeEventEnvelope, + PurposeUpdatedV1, + PurposeVersion, + PurposeVersionActivatedV1, + PurposeVersionArchivedV1, + PurposeVersionCreatedV1, + PurposeVersionDeletedV1, + PurposeVersionRejectedV1, + PurposeVersionSuspendedV1, + PurposeVersionUpdatedV1, + PurposeVersionWaitedForApprovalV1, + generateId, + purposeVersionState, + toPurposeV1, + toPurposeVersionV1, +} from "pagopa-interop-models"; +import { handleMessageV1 } from "../src/purposeConsumerServiceV1.js"; describe("Integration tests", async () => { let purposes: PurposeCollection; @@ -33,38 +57,409 @@ describe("Integration tests", async () => { }); describe("Events V1", () => { - it("PurposeCreated", () => { - expect(1).toBe(1); + const mockPurpose = getMockPurpose(); + + it("PurposeCreated", async () => { + const payload: PurposeCreatedV1 = { + purpose: toPurposeV1(mockPurpose), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeCreated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: mockPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionCreated", () => { - expect(1).toBe(1); + + it("PurposeVersionCreated", async () => { + await writeInReadmodel(mockPurpose, purposes, 1); + + const mockPurposeVersion = getMockPurposeVersion(); + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + const payload: PurposeVersionCreatedV1 = { + purposeId: mockPurpose.id, + version: toPurposeVersionV1(mockPurposeVersion), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionCreated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeUpdated", () => { - expect(1).toBe(1); + + it("PurposeUpdated", async () => { + await writeInReadmodel(mockPurpose, purposes, 1); + + const updatedPurpose = { + ...mockPurpose, + description: "Updated description", + }; + const payload: PurposeUpdatedV1 = { + purpose: toPurposeV1(updatedPurpose), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionActivated", () => { - expect(1).toBe(1); + + it("PurposeVersionActivated", async () => { + const mockPurposeVersion = getMockPurposeVersion(); + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { ...mockPurposeVersion, state: purposeVersionState.active }, + ], + }; + const payload: PurposeVersionActivatedV1 = { + purpose: toPurposeV1(updatedPurpose), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionSuspended", () => { - expect(1).toBe(1); + + it("PurposeVersionSuspended", async () => { + const mockPurposeVersion = getMockPurposeVersion(); + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { ...mockPurposeVersion, state: purposeVersionState.suspended }, + ], + }; + const payload: PurposeVersionSuspendedV1 = { + purpose: toPurposeV1(updatedPurpose), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionSuspended", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionArchived", () => { - expect(1).toBe(1); + + it("PurposeVersionArchived", async () => { + const mockPurposeVersion = getMockPurposeVersion(); + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { ...mockPurposeVersion, state: purposeVersionState.archived }, + ], + }; + const payload: PurposeVersionArchivedV1 = { + purpose: toPurposeV1(updatedPurpose), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionArchived", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionWaitedForApproval", () => { - expect(1).toBe(1); + + it("PurposeVersionWaitedForApproval", async () => { + const mockPurposeVersion = getMockPurposeVersion(); + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.waitingForApproval, + }, + ], + }; + const payload: PurposeVersionWaitedForApprovalV1 = { + purpose: toPurposeV1(updatedPurpose), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionWaitedForApproval", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionRejected", () => { - expect(1).toBe(1); + + it("PurposeVersionRejected", async () => { + const mockPurposeVersion = getMockPurposeVersion(); + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.rejected, + }, + ], + }; + const payload: PurposeVersionRejectedV1 = { + purpose: toPurposeV1(updatedPurpose), + versionId: mockPurposeVersion.id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionRejected", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeVersionUpdated", () => { - expect(1).toBe(1); + + it("PurposeVersionUpdated", async () => { + const mockPurposeVersion = getMockPurposeVersion(); + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurposeVersion: PurposeVersion = { + ...mockPurposeVersion, + rejectionReason: "new rejection reason", + }; + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [updatedPurposeVersion], + }; + const payload: PurposeVersionUpdatedV1 = { + purposeId: purpose.id, + version: toPurposeVersionV1(updatedPurposeVersion), + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); - it("PurposeDeleted", () => { - expect(1).toBe(1); + + it("PurposeDeleted", async () => { + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "Purpose 2 - test", + }; + await writeInReadmodel(mockPurpose, purposes, 1); + await writeInReadmodel(mockPurpose2, purposes, 1); + + const payload: PurposeDeletedV1 = { + purposeId: mockPurpose.id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + const retrievedPurpose2 = await purposes.findOne({ + "data.id": mockPurpose2.id, + }); + + expect(retrievedPurpose).toBeUndefined(); + expect(retrievedPurpose2).toMatchObject({ + data: mockPurpose2, + metadata: { version: 1 }, + }); }); - it("PurposeVersionDeleted", () => { - expect(1).toBe(1); + + it("PurposeVersionDeleted", async () => { + const mockPurposeVersion1 = getMockPurposeVersion(); + const mockPurposeVersion2 = getMockPurposeVersion(); + + const purpose = { + ...mockPurpose, + versions: [mockPurposeVersion1, mockPurposeVersion2], + }; + await writeInReadmodel(purpose, purposes, 1); + + const updatedPurpose: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion2], + }; + const payload: PurposeVersionDeletedV1 = { + purposeId: purpose.id, + versionId: mockPurposeVersion1.id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + type: "PurposeVersionDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + await handleMessageV1(message, purposes); + + const retrievedPurpose = await purposes.findOne({ + "data.id": mockPurpose.id, + }); + + expect(retrievedPurpose).toMatchObject({ + data: updatedPurpose, + metadata: { version: 1 }, + }); }); }); @@ -74,3 +469,22 @@ describe("Integration tests", async () => { }); }); }); + +const getMockPurpose = (): Purpose => ({ + id: generateId(), + title: "Purpose 1 - test", + createdAt: new Date(), + eserviceId: generateId(), + consumerId: generateId(), + updatedAt: undefined, + description: "Test purpose - description", + versions: [], + isFreeOfCharge: false, +}); + +const getMockPurposeVersion = (): PurposeVersion => ({ + id: generateId(), + createdAt: new Date(), + state: "Draft", + dailyCalls: 10, +}); From f147aafbcc7caf2f4b6f50eb43c0b20b22714637 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:35:37 +0200 Subject: [PATCH 050/537] Improve test data --- .../test/consumerService.integration.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index 67f8f28979..881b839dd8 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -484,7 +484,13 @@ const getMockPurpose = (): Purpose => ({ const getMockPurposeVersion = (): PurposeVersion => ({ id: generateId(), - createdAt: new Date(), state: "Draft", + riskAnalysis: { + id: generateId(), + contentType: "json", + path: "path", + createdAt: new Date(), + }, dailyCalls: 10, + createdAt: new Date(), }); From 37468b68a4e7d3bff0da300419c9cf61f0551109 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:50:55 +0200 Subject: [PATCH 051/537] Fix consumer --- .../purpose-readmodel-writer/src/purposeConsumerServiceV1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts index f6f7f2bb47..e49a6200bd 100644 --- a/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts +++ b/packages/purpose-readmodel-writer/src/purposeConsumerServiceV1.ts @@ -58,7 +58,7 @@ export async function handleMessageV1( await purposes.updateOne( { "data.id": msg.stream_id }, { - $setOnInsert: { + $set: { data: msg.data.purpose ? fromPurposeV1(msg.data.purpose) : undefined, From ae0fbc8c7ff446cfe56aef4fa05f99e6b6a874fd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:50:59 +0200 Subject: [PATCH 052/537] Fix tests --- .../test/consumerService.integration.test.ts | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index 881b839dd8..06e2790984 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -130,7 +130,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeUpdated", event_version: 1, data: payload, @@ -144,7 +144,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -168,7 +168,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionActivated", event_version: 1, data: payload, @@ -182,7 +182,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -206,7 +206,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionSuspended", event_version: 1, data: payload, @@ -220,7 +220,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -244,7 +244,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionArchived", event_version: 1, data: payload, @@ -258,7 +258,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -285,7 +285,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionWaitedForApproval", event_version: 1, data: payload, @@ -299,7 +299,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -327,7 +327,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionRejected", event_version: 1, data: payload, @@ -341,7 +341,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -368,7 +368,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionUpdated", event_version: 1, data: payload, @@ -382,7 +382,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); @@ -401,7 +401,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeDeleted", event_version: 1, data: payload, @@ -416,7 +416,7 @@ describe("Integration tests", async () => { "data.id": mockPurpose2.id, }); - expect(retrievedPurpose).toBeUndefined(); + expect(retrievedPurpose?.data).toBeUndefined(); expect(retrievedPurpose2).toMatchObject({ data: mockPurpose2, metadata: { version: 1 }, @@ -444,7 +444,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionDeleted", event_version: 1, data: payload, @@ -458,7 +458,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); }); @@ -476,10 +476,9 @@ const getMockPurpose = (): Purpose => ({ createdAt: new Date(), eserviceId: generateId(), consumerId: generateId(), - updatedAt: undefined, description: "Test purpose - description", versions: [], - isFreeOfCharge: false, + isFreeOfCharge: true, }); const getMockPurposeVersion = (): PurposeVersion => ({ From 65365e5a45abb340a5eb785684ac65f705e2056f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:53:42 +0200 Subject: [PATCH 053/537] Fix test --- .../test/consumerService.integration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index 06e2790984..0f1a0878f2 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -99,7 +99,7 @@ describe("Integration tests", async () => { const message: PurposeEventEnvelope = { sequence_num: 1, stream_id: mockPurpose.id, - version: 1, + version: 2, type: "PurposeVersionCreated", event_version: 1, data: payload, @@ -113,7 +113,7 @@ describe("Integration tests", async () => { expect(retrievedPurpose).toMatchObject({ data: updatedPurpose, - metadata: { version: 1 }, + metadata: { version: 2 }, }); }); From 79a637229d9f9956cbbab2b11d2aa4d2d26415b4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:56:06 +0200 Subject: [PATCH 054/537] Minor change --- .../test/consumerService.integration.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index 0f1a0878f2..4d4670256f 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -472,12 +472,12 @@ describe("Integration tests", async () => { const getMockPurpose = (): Purpose => ({ id: generateId(), - title: "Purpose 1 - test", - createdAt: new Date(), eserviceId: generateId(), consumerId: generateId(), - description: "Test purpose - description", versions: [], + title: "Purpose 1 - test", + description: "Test purpose - description", + createdAt: new Date(), isFreeOfCharge: true, }); From 03da64b177646f637942e8f9267099941377895e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 11:58:19 +0200 Subject: [PATCH 055/537] Refactor --- packages/commons-test/src/testUtils.ts | 26 +++++++++++++ .../test/consumerService.integration.test.ts | 39 +++---------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 06dc88b509..e045f98773 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -11,6 +11,8 @@ import { EService, EServiceAttribute, EServiceId, + Purpose, + PurposeVersion, Tenant, TenantAttribute, TenantId, @@ -144,3 +146,27 @@ export const getMockAttribute = (): Attribute => ({ code: undefined, origin: undefined, }); + +export const getMockPurpose = (): Purpose => ({ + id: generateId(), + eserviceId: generateId(), + consumerId: generateId(), + versions: [], + title: "Purpose 1 - test", + description: "Test purpose - description", + createdAt: new Date(), + isFreeOfCharge: true, +}); + +export const getMockPurposeVersion = (): PurposeVersion => ({ + id: generateId(), + state: "Draft", + riskAnalysis: { + id: generateId(), + contentType: "json", + path: "path", + createdAt: new Date(), + }, + dailyCalls: 10, + createdAt: new Date(), +}); diff --git a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts index 4d4670256f..6dd417ac86 100644 --- a/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts +++ b/packages/purpose-readmodel-writer/test/consumerService.integration.test.ts @@ -7,6 +7,8 @@ import { readModelWriterConfig, } from "pagopa-interop-commons"; import { + getMockPurpose, + getMockPurposeVersion, mongoDBContainer, writeInReadmodel, } from "pagopa-interop-commons-test"; @@ -58,6 +60,7 @@ describe("Integration tests", async () => { describe("Events V1", () => { const mockPurpose = getMockPurpose(); + const mockPurposeVersion = getMockPurposeVersion(); it("PurposeCreated", async () => { const payload: PurposeCreatedV1 = { @@ -87,7 +90,6 @@ describe("Integration tests", async () => { it("PurposeVersionCreated", async () => { await writeInReadmodel(mockPurpose, purposes, 1); - const mockPurposeVersion = getMockPurposeVersion(); const updatedPurpose: Purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -149,7 +151,6 @@ describe("Integration tests", async () => { }); it("PurposeVersionActivated", async () => { - const mockPurposeVersion = getMockPurposeVersion(); const purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -187,7 +188,6 @@ describe("Integration tests", async () => { }); it("PurposeVersionSuspended", async () => { - const mockPurposeVersion = getMockPurposeVersion(); const purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -225,7 +225,6 @@ describe("Integration tests", async () => { }); it("PurposeVersionArchived", async () => { - const mockPurposeVersion = getMockPurposeVersion(); const purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -263,7 +262,6 @@ describe("Integration tests", async () => { }); it("PurposeVersionWaitedForApproval", async () => { - const mockPurposeVersion = getMockPurposeVersion(); const purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -304,7 +302,6 @@ describe("Integration tests", async () => { }); it("PurposeVersionRejected", async () => { - const mockPurposeVersion = getMockPurposeVersion(); const purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -346,7 +343,6 @@ describe("Integration tests", async () => { }); it("PurposeVersionUpdated", async () => { - const mockPurposeVersion = getMockPurposeVersion(); const purpose = { ...mockPurpose, versions: [mockPurposeVersion], @@ -424,12 +420,11 @@ describe("Integration tests", async () => { }); it("PurposeVersionDeleted", async () => { - const mockPurposeVersion1 = getMockPurposeVersion(); const mockPurposeVersion2 = getMockPurposeVersion(); const purpose = { ...mockPurpose, - versions: [mockPurposeVersion1, mockPurposeVersion2], + versions: [mockPurposeVersion, mockPurposeVersion2], }; await writeInReadmodel(purpose, purposes, 1); @@ -439,7 +434,7 @@ describe("Integration tests", async () => { }; const payload: PurposeVersionDeletedV1 = { purposeId: purpose.id, - versionId: mockPurposeVersion1.id, + versionId: mockPurposeVersion.id, }; const message: PurposeEventEnvelope = { sequence_num: 1, @@ -469,27 +464,3 @@ describe("Integration tests", async () => { }); }); }); - -const getMockPurpose = (): Purpose => ({ - id: generateId(), - eserviceId: generateId(), - consumerId: generateId(), - versions: [], - title: "Purpose 1 - test", - description: "Test purpose - description", - createdAt: new Date(), - isFreeOfCharge: true, -}); - -const getMockPurposeVersion = (): PurposeVersion => ({ - id: generateId(), - state: "Draft", - riskAnalysis: { - id: generateId(), - contentType: "json", - path: "path", - createdAt: new Date(), - }, - dailyCalls: 10, - createdAt: new Date(), -}); From f5018a16b4784f47af5b6c8bf89f10c2ad7b793f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 14:15:48 +0200 Subject: [PATCH 056/537] Refactor --- packages/models/src/brandedIds.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 2506c2d96f..1c7c7c23ae 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -47,14 +47,6 @@ export type RiskAnalysisFormId = z.infer; export const RiskAnalysisId = z.string().uuid().brand("RiskAnalysisId"); export type RiskAnalysisId = z.infer; -export const PurposeRiskAnalysisFormId = z - .string() - .uuid() - .brand("PurposeRiskAnalysisFormId"); -export type PurposeRiskAnalysisFormId = z.infer< - typeof PurposeRiskAnalysisFormId ->; - export const PurposeId = z.string().uuid().brand("PurposeId"); export type PurposeId = z.infer; @@ -67,6 +59,14 @@ export const PurposeVersionDocumentId = z .brand("PurposeVersionDocumentId"); export type PurposeVersionDocumentId = z.infer; +export const PurposeRiskAnalysisFormId = z + .string() + .uuid() + .brand("PurposeRiskAnalysisFormId"); +export type PurposeRiskAnalysisFormId = z.infer< + typeof PurposeRiskAnalysisFormId +>; + type IDS = | EServiceId | EServiceDocumentId From 39144d53fb4a70d09da787467040b4496080a73f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 14:16:58 +0200 Subject: [PATCH 057/537] Update pnpm-lock --- pnpm-lock.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 932a31d3a0..5d8c74ef42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4838,6 +4838,16 @@ packages: pretty-format: 29.6.1 dev: true + /@zodios/core@10.9.2(axios@1.4.0)(zod@3.21.4): + resolution: {integrity: sha512-aY8FgE6Y+91oBaMfTnqibGoHO62OiKKHoiQAq76PYJbU7InFeWsSRhpnBwpibWopRjz45AlqojxKo3QeSAYC7w==} + peerDependencies: + axios: ^0.x || ^1.0.0 + zod: ^3.x + dependencies: + axios: 1.4.0 + zod: 3.21.4 + dev: false + /@zodios/core@10.9.6(axios@1.4.0)(zod@3.21.4): resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} peerDependencies: @@ -4856,6 +4866,18 @@ packages: axios: 1.6.5 zod: 3.21.4 + /@zodios/express@10.6.1(@zodios/core@10.9.2)(express@4.18.2)(zod@3.21.4): + resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} + peerDependencies: + '@zodios/core': '>=10.4.4 <11.0.0' + express: 4.x + zod: ^3.x + dependencies: + '@zodios/core': 10.9.2(axios@1.4.0)(zod@3.21.4) + express: 4.18.2 + zod: 3.21.4 + dev: false + /@zodios/express@10.6.1(@zodios/core@10.9.6)(express@4.18.2)(zod@3.21.4): resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} peerDependencies: From 491242af3c569816d09820a87e68bda42e53ec2d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 14:23:48 +0200 Subject: [PATCH 058/537] Fix date --- packages/models/src/purpose/protobufConverterFromV1.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index 82c6d21df6..f4551b0ec1 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -59,7 +59,9 @@ export const fromPurposeVersionV1 = ( : undefined, createdAt: new Date(Number(input.createdAt)), updatedAt: input.updatedAt ? new Date(Number(input.updatedAt)) : undefined, - firstActivationAt: new Date(Number(input.updatedAt)), + firstActivationAt: input.firstActivationAt + ? new Date(Number(input.firstActivationAt)) + : undefined, suspendedAt: input.suspendedAt ? new Date(Number(input.suspendedAt)) : undefined, From 677200e71d51bb1192968f45d9f1156d3986e3de Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 14:39:36 +0200 Subject: [PATCH 059/537] Refactor --- .../src/purpose/protobufConverterFromV1.ts | 19 +++++++------------ .../src/purpose/protobufConverterToV1.ts | 17 ++++++----------- packages/models/src/utils.ts | 9 +++++++++ 3 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 packages/models/src/utils.ts diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index f4551b0ec1..c715c43b63 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -7,6 +7,7 @@ import { import { RiskAnalysisId, unsafeBrandId } from "../brandedIds.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; import { PurposeRiskAnalysisFormV1 } from "../gen/v1/purpose/riskAnalysis.js"; +import { bigIntToDate, bigIntToDateOrUndefined } from "../utils.js"; import { Purpose, PurposeVersion, @@ -51,20 +52,14 @@ export const fromPurposeVersionV1 = ( ...input, id: unsafeBrandId(input.id), state: fromPurposeVersionStateV1(input.state), - expectedApprovalDate: input.expectedApprovalDate - ? new Date(Number(input.expectedApprovalDate)) - : undefined, + expectedApprovalDate: bigIntToDateOrUndefined(input.expectedApprovalDate), riskAnalysis: input.riskAnalysis ? fromPurposeVersionDocumentV1(input.riskAnalysis) : undefined, - createdAt: new Date(Number(input.createdAt)), - updatedAt: input.updatedAt ? new Date(Number(input.updatedAt)) : undefined, - firstActivationAt: input.firstActivationAt - ? new Date(Number(input.firstActivationAt)) - : undefined, - suspendedAt: input.suspendedAt - ? new Date(Number(input.suspendedAt)) - : undefined, + createdAt: bigIntToDate(input.createdAt), + updatedAt: bigIntToDateOrUndefined(input.updatedAt), + firstActivationAt: bigIntToDateOrUndefined(input.firstActivationAt), + suspendedAt: bigIntToDateOrUndefined(input.suspendedAt), }); export const fromPurposeRiskAnalysisFormV1 = ( @@ -93,7 +88,7 @@ export const fromPurposeV1 = (input: PurposeV1): Purpose => ({ versions: input.versions.map(fromPurposeVersionV1), isFreeOfCharge: input.isFreeOfCharge || true, createdAt: new Date(Number(input.createdAt)), - updatedAt: input.updatedAt ? new Date(Number(input.updatedAt)) : undefined, + updatedAt: bigIntToDateOrUndefined(input.updatedAt), riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV1(input.riskAnalysisForm) : undefined, diff --git a/packages/models/src/purpose/protobufConverterToV1.ts b/packages/models/src/purpose/protobufConverterToV1.ts index c0b83fcb09..3c2198c8a0 100644 --- a/packages/models/src/purpose/protobufConverterToV1.ts +++ b/packages/models/src/purpose/protobufConverterToV1.ts @@ -6,6 +6,7 @@ import { PurposeVersionV1, } from "../gen/v1/purpose/purpose.js"; +import { dateToBigIntOrUndefined } from "../utils.js"; import { Purpose, PurposeVersion, @@ -41,17 +42,11 @@ export const toPurposeVersionV1 = ( ): PurposeVersionV1 => ({ ...input, state: toPurposeVersionStateV1(input.state), - expectedApprovalDate: input.expectedApprovalDate - ? BigInt(input.expectedApprovalDate.getTime()) - : undefined, + expectedApprovalDate: dateToBigIntOrUndefined(input.expectedApprovalDate), createdAt: BigInt(input.createdAt.getTime()), - updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, - firstActivationAt: input.firstActivationAt - ? BigInt(input.firstActivationAt.getTime()) - : undefined, - suspendedAt: input.suspendedAt - ? BigInt(input.suspendedAt.getTime()) - : undefined, + updatedAt: dateToBigIntOrUndefined(input.updatedAt), + firstActivationAt: dateToBigIntOrUndefined(input.firstActivationAt), + suspendedAt: dateToBigIntOrUndefined(input.suspendedAt), riskAnalysis: input.riskAnalysis ? toPurposeVersionDocumentV1(input.riskAnalysis) : undefined, @@ -61,5 +56,5 @@ export const toPurposeV1 = (input: Purpose): PurposeV1 => ({ ...input, versions: input.versions.map(toPurposeVersionV1), createdAt: BigInt(input.createdAt.getTime()), - updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, + updatedAt: dateToBigIntOrUndefined(input.updatedAt), }); diff --git a/packages/models/src/utils.ts b/packages/models/src/utils.ts new file mode 100644 index 0000000000..bf3bd653ae --- /dev/null +++ b/packages/models/src/utils.ts @@ -0,0 +1,9 @@ +export const bigIntToDateOrUndefined = ( + input: bigint | undefined +): Date | undefined => (input ? new Date(Number(input)) : undefined); + +export const bigIntToDate = (input: bigint): Date => new Date(Number(input)); + +export const dateToBigIntOrUndefined = ( + input: Date | undefined +): bigint | undefined => (input ? BigInt(input.getTime()) : undefined); From 2a6a3d76c1f3589adbf1a6cfd66f2870584176e7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 14:43:59 +0200 Subject: [PATCH 060/537] Refactor --- packages/models/src/purpose/protobufConverterFromV1.ts | 4 ++-- packages/models/src/purpose/protobufConverterToV1.ts | 8 ++++---- packages/models/src/utils.ts | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index c715c43b63..10ceb6ced2 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -43,7 +43,7 @@ export const fromPurposeVersionDocumentV1 = ( ): PurposeVersionDocument => ({ ...input, id: unsafeBrandId(input.id), - createdAt: new Date(Number(input.createdAt)), + createdAt: bigIntToDate(input.createdAt), }); export const fromPurposeVersionV1 = ( @@ -87,7 +87,7 @@ export const fromPurposeV1 = (input: PurposeV1): Purpose => ({ consumerId: unsafeBrandId(input.consumerId), versions: input.versions.map(fromPurposeVersionV1), isFreeOfCharge: input.isFreeOfCharge || true, - createdAt: new Date(Number(input.createdAt)), + createdAt: bigIntToDate(input.createdAt), updatedAt: bigIntToDateOrUndefined(input.updatedAt), riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV1(input.riskAnalysisForm) diff --git a/packages/models/src/purpose/protobufConverterToV1.ts b/packages/models/src/purpose/protobufConverterToV1.ts index 3c2198c8a0..2107bbef65 100644 --- a/packages/models/src/purpose/protobufConverterToV1.ts +++ b/packages/models/src/purpose/protobufConverterToV1.ts @@ -6,7 +6,7 @@ import { PurposeVersionV1, } from "../gen/v1/purpose/purpose.js"; -import { dateToBigIntOrUndefined } from "../utils.js"; +import { dateToBigInt, dateToBigIntOrUndefined } from "../utils.js"; import { Purpose, PurposeVersion, @@ -34,7 +34,7 @@ export const toPurposeVersionDocumentV1 = ( input: PurposeVersionDocument ): PurposeVersionDocumentV1 => ({ ...input, - createdAt: BigInt(input.createdAt.getTime()), + createdAt: dateToBigInt(input.createdAt), }); export const toPurposeVersionV1 = ( @@ -43,7 +43,7 @@ export const toPurposeVersionV1 = ( ...input, state: toPurposeVersionStateV1(input.state), expectedApprovalDate: dateToBigIntOrUndefined(input.expectedApprovalDate), - createdAt: BigInt(input.createdAt.getTime()), + createdAt: dateToBigInt(input.createdAt), updatedAt: dateToBigIntOrUndefined(input.updatedAt), firstActivationAt: dateToBigIntOrUndefined(input.firstActivationAt), suspendedAt: dateToBigIntOrUndefined(input.suspendedAt), @@ -55,6 +55,6 @@ export const toPurposeVersionV1 = ( export const toPurposeV1 = (input: Purpose): PurposeV1 => ({ ...input, versions: input.versions.map(toPurposeVersionV1), - createdAt: BigInt(input.createdAt.getTime()), + createdAt: dateToBigInt(input.createdAt), updatedAt: dateToBigIntOrUndefined(input.updatedAt), }); diff --git a/packages/models/src/utils.ts b/packages/models/src/utils.ts index bf3bd653ae..bcf824c9ec 100644 --- a/packages/models/src/utils.ts +++ b/packages/models/src/utils.ts @@ -7,3 +7,5 @@ export const bigIntToDate = (input: bigint): Date => new Date(Number(input)); export const dateToBigIntOrUndefined = ( input: Date | undefined ): bigint | undefined => (input ? BigInt(input.getTime()) : undefined); + +export const dateToBigInt = (input: Date): bigint => BigInt(input.getTime()); From 4e1f2370c9edd6f39508e12e39f6a828e0dd779c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 14:50:47 +0200 Subject: [PATCH 061/537] Fix and refactor dates --- .../src/purpose/protobufConverterFromV2.ts | 15 ++++++------ .../src/purpose/protobufConverterToV2.ts | 23 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index bca21b29cd..b48e8a157e 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -7,6 +7,7 @@ import { } from "../gen/v2/purpose/purpose.js"; import { PurposeRiskAnalysisFormV2 } from "../gen/v2/purpose/riskAnalysis.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; +import { bigIntToDate, bigIntToDateOrUndefined } from "../utils.js"; import { Purpose, PurposeVersion, @@ -39,7 +40,7 @@ export const fromPurposeVersionDocumentV2 = ( ): PurposeVersionDocument => ({ ...input, id: unsafeBrandId(input.id), - createdAt: new Date(Number(input.createdAt)), + createdAt: bigIntToDate(input.createdAt), }); export const fromPurposeVersionV2 = ( @@ -54,10 +55,10 @@ export const fromPurposeVersionV2 = ( riskAnalysis: input.riskAnalysis ? fromPurposeVersionDocumentV2(input.riskAnalysis) : undefined, - createdAt: new Date(Number(input.createdAt)), - updatedAt: new Date(Number(input.updatedAt)), - firstActivationAt: new Date(Number(input.updatedAt)), - suspendedAt: new Date(Number(input.suspendedAt)), + createdAt: bigIntToDate(input.createdAt), + updatedAt: bigIntToDateOrUndefined(input.updatedAt), + firstActivationAt: bigIntToDateOrUndefined(input.firstActivationAt), + suspendedAt: bigIntToDateOrUndefined(input.suspendedAt), }); export const fromPurposeRiskAnalysisFormV2 = ( @@ -85,8 +86,8 @@ export const fromPurposeV2 = (input: PurposeV2): Purpose => ({ consumerId: unsafeBrandId(input.consumerId), versions: input.versions.map(fromPurposeVersionV2), isFreeOfCharge: input.isFreeOfCharge || true, - createdAt: new Date(Number(input.createdAt)), - updatedAt: new Date(Number(input.updatedAt)), + createdAt: bigIntToDate(input.createdAt), + updatedAt: bigIntToDateOrUndefined(input.createdAt), riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV2(input.riskAnalysisForm) : undefined, diff --git a/packages/models/src/purpose/protobufConverterToV2.ts b/packages/models/src/purpose/protobufConverterToV2.ts index d200eb2364..4556788f5e 100644 --- a/packages/models/src/purpose/protobufConverterToV2.ts +++ b/packages/models/src/purpose/protobufConverterToV2.ts @@ -5,6 +5,7 @@ import { PurposeVersionDocumentV2, PurposeVersionV2, } from "../gen/v2/purpose/purpose.js"; +import { dateToBigInt, dateToBigIntOrUndefined } from "../utils.js"; import { Purpose, PurposeVersion, @@ -32,7 +33,7 @@ export const toPurposeVersionDocumentV2 = ( input: PurposeVersionDocument ): PurposeVersionDocumentV2 => ({ ...input, - createdAt: BigInt(input.createdAt.getTime()), + createdAt: dateToBigInt(input.createdAt), }); export const toPurposeVersionV2 = ( @@ -40,17 +41,11 @@ export const toPurposeVersionV2 = ( ): PurposeVersionV2 => ({ ...input, state: toPurposeVersionStateV2(input.state), - expectedApprovalDate: input.expectedApprovalDate - ? BigInt(input.expectedApprovalDate.getTime()) - : undefined, - createdAt: BigInt(input.createdAt.getTime()), - updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, - firstActivationAt: input.firstActivationAt - ? BigInt(input.firstActivationAt.getTime()) - : undefined, - suspendedAt: input.suspendedAt - ? BigInt(input.suspendedAt.getTime()) - : undefined, + expectedApprovalDate: dateToBigIntOrUndefined(input.expectedApprovalDate), + createdAt: dateToBigInt(input.createdAt), + updatedAt: dateToBigIntOrUndefined(input.updatedAt), + firstActivationAt: dateToBigIntOrUndefined(input.firstActivationAt), + suspendedAt: dateToBigIntOrUndefined(input.suspendedAt), riskAnalysis: input.riskAnalysis ? toPurposeVersionDocumentV2(input.riskAnalysis) : undefined, @@ -59,6 +54,6 @@ export const toPurposeVersionV2 = ( export const toPurposeV2 = (input: Purpose): PurposeV2 => ({ ...input, versions: input.versions.map(toPurposeVersionV2), - createdAt: BigInt(input.createdAt.getTime()), - updatedAt: input.updatedAt ? BigInt(input.updatedAt.getTime()) : undefined, + createdAt: dateToBigInt(input.createdAt), + updatedAt: dateToBigIntOrUndefined(input.updatedAt), }); From 38b5bab7a6350d493e02ef6b5ca1e6e54a70369e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 15:01:33 +0200 Subject: [PATCH 062/537] Refactor --- packages/models/src/brandedIds.ts | 11 +---------- packages/models/src/risk-analysis/riskAnalysis.ts | 12 ++++-------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 1c7c7c23ae..e816440e28 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -59,14 +59,6 @@ export const PurposeVersionDocumentId = z .brand("PurposeVersionDocumentId"); export type PurposeVersionDocumentId = z.infer; -export const PurposeRiskAnalysisFormId = z - .string() - .uuid() - .brand("PurposeRiskAnalysisFormId"); -export type PurposeRiskAnalysisFormId = z.infer< - typeof PurposeRiskAnalysisFormId ->; - type IDS = | EServiceId | EServiceDocumentId @@ -81,8 +73,7 @@ type IDS = | RiskAnalysisId | PurposeId | PurposeVersionId - | PurposeVersionDocumentId - | PurposeRiskAnalysisFormId; + | PurposeVersionDocumentId; // This function is used to generate a new ID for a new object // it infers the type of the ID based on how is used the result diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 11e68f3692..77c40d1bf4 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -1,6 +1,5 @@ import { z } from "zod"; import { - PurposeRiskAnalysisFormId, RiskAnalysisFormId, RiskAnalysisId, RiskAnalysisMultiAnswerId, @@ -29,13 +28,10 @@ export const RiskAnalysisForm = z.object({ }); export type RiskAnalysisForm = z.infer; -export const PurposeRiskAnalysisForm = z.object({ - id: PurposeRiskAnalysisFormId, - riskAnalysisId: RiskAnalysisId.optional(), - version: z.string(), - singleAnswers: z.array(RiskAnalysisSingleAnswer), - multiAnswers: z.array(RiskAnalysisMultiAnswer), -}); +export const PurposeRiskAnalysisForm = z.intersection( + RiskAnalysisForm, + RiskAnalysisId.optional() +); export type PurposeRiskAnalysisForm = z.infer; export const RiskAnalysis = z.object({ From 309253e35f405dbee2d92fa8bd5b2cf47092305c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 15:15:37 +0200 Subject: [PATCH 063/537] Fix refactor --- packages/models/src/risk-analysis/riskAnalysis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 77c40d1bf4..7970c18847 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -28,10 +28,10 @@ export const RiskAnalysisForm = z.object({ }); export type RiskAnalysisForm = z.infer; -export const PurposeRiskAnalysisForm = z.intersection( - RiskAnalysisForm, - RiskAnalysisId.optional() -); +export const PurposeRiskAnalysisForm = z.object({ + ...RiskAnalysisForm.shape, + riskAnalysisId: RiskAnalysisId.optional(), +}); export type PurposeRiskAnalysisForm = z.infer; export const RiskAnalysis = z.object({ From 11fabfeecfbd15a376b21cf1aaa15079cd23eed0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 15:24:33 +0200 Subject: [PATCH 064/537] Delete gitignore --- packages/purpose-readmodel-writer/.gitignore | 44 -------------------- 1 file changed, 44 deletions(-) delete mode 100644 packages/purpose-readmodel-writer/.gitignore diff --git a/packages/purpose-readmodel-writer/.gitignore b/packages/purpose-readmodel-writer/.gitignore deleted file mode 100644 index dece5f575e..0000000000 --- a/packages/purpose-readmodel-writer/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# Created by https://www.toptal.com/developers/gitignore/api/node,visualstudiocode -# Edit at https://www.toptal.com/developers/gitignore?templates=node,visualstudiocode - -### Node ### -# Logs -logs -*.log -.pnpm-debug.log* - -# Dependency directories -node_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Output of 'npm pack' -*.tgz - - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -dist From 80663482cc1dd7204be51cc0c1912891970885f8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 9 Apr 2024 16:38:12 +0200 Subject: [PATCH 065/537] Update isFreeOfCharge --- packages/models/proto/v2/purpose/purpose.proto | 2 +- packages/models/src/purpose/protobufConverterFromV2.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/models/proto/v2/purpose/purpose.proto b/packages/models/proto/v2/purpose/purpose.proto index f7f467b182..e8a191efd5 100644 --- a/packages/models/proto/v2/purpose/purpose.proto +++ b/packages/models/proto/v2/purpose/purpose.proto @@ -16,7 +16,7 @@ message PurposeV2 { optional PurposeRiskAnalysisFormV2 riskAnalysisForm = 10; int64 createdAt = 11; optional int64 updatedAt = 12; - optional bool isFreeOfCharge = 13; + bool isFreeOfCharge = 13; optional string freeOfChargeReason = 14; } diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index b48e8a157e..44620f5786 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -85,7 +85,7 @@ export const fromPurposeV2 = (input: PurposeV2): Purpose => ({ eserviceId: unsafeBrandId(input.eserviceId), consumerId: unsafeBrandId(input.consumerId), versions: input.versions.map(fromPurposeVersionV2), - isFreeOfCharge: input.isFreeOfCharge || true, + isFreeOfCharge: input.isFreeOfCharge, createdAt: bigIntToDate(input.createdAt), updatedAt: bigIntToDateOrUndefined(input.createdAt), riskAnalysisForm: input.riskAnalysisForm From a2cf152163cfb4fda75f7ddab656a32086818c9f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 12:10:17 +0200 Subject: [PATCH 066/537] Add latest changes in api spec --- .../open-api/purpose-service-spec.yml | 81 ++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index c54d027e0a..bce802f136 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -446,6 +446,54 @@ paths: application/problem+json: schema: $ref: '#/components/schemas/Problem' + /purposes/{purposeId}/versions/{versionId}/reject: + parameters: + - $ref: '#/components/parameters/CorrelationIdHeader' + - name: purposeId + in: path + required: true + schema: + type: string + format: uuid + - name: versionId + in: path + required: true + schema: + type: string + format: uuid + post: + summary: Rejected Purpose Version + operationId: rejectPurposeVersion + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RejectPurposeVersionPayload' + required: true + responses: + '204': + description: Purpose Version Rejected + '400': + description: Bad Request + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '403': + description: Forbidden + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + '404': + description: Purpose not found + content: + application/problem+json: + schema: + $ref: '#/components/schemas/Problem' + description: reject the purpose version by id + tags: + - purpose /purposes/{purposeId}/versions/{versionId}/activate: parameters: - $ref: '#/components/parameters/CorrelationIdHeader' @@ -498,6 +546,12 @@ paths: post: summary: Clone Purpose operationId: clonePurpose + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PurposeCloneSeed' + required: true responses: '200': description: Purpose Cloned @@ -696,10 +750,12 @@ paths: required: true schema: type: string - - name: tenantKind + - name: eserviceId in: query + required: true schema: - $ref: '#/components/schemas/TenantKind' + type: string + format: uuid get: tags: - purpose @@ -733,6 +789,16 @@ components: schema: type: string schemas: + RejectPurposeVersionPayload: + type: object + description: models the reject payload for this purpose version. + properties: + rejectionReason: + type: string + minimum: 10 + maximum: 250 + required: + - rejectionReason TenantKind: type: string description: Tenant Kind @@ -1096,6 +1162,8 @@ components: minimum: 0 riskAnalysis: $ref: '#/components/schemas/PurposeVersionDocument' + rejectionReason: + type: string required: - id - state @@ -1111,6 +1179,7 @@ components: - SUSPENDED - WAITING_FOR_APPROVAL - ARCHIVED + - REJECTED PurposeVersionDocument: type: object required: @@ -1129,6 +1198,14 @@ components: createdAt: type: string format: date-time + PurposeCloneSeed: + type: object + properties: + eserviceId: + type: string + format: uuid + required: + - eserviceId EServicePurposeSeed: type: object description: contains the expected payload for purpose creation. From bcde037aac66e790cd643f8099973286e71b5724 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 12:11:52 +0200 Subject: [PATCH 067/537] Add missing endpoint --- packages/purpose-process/src/routers/PurposeRouter.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index b96ea5cec7..0941b94376 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -56,6 +56,11 @@ const purposeRouter = ( authorizationMiddleware([ADMIN_ROLE]), (_req, res) => res.status(501).send() ) + .post( + "/purposes/:purposeId/versions/:versionId/reject", + authorizationMiddleware([ADMIN_ROLE]), + (_req, res) => res.status(501).send() + ) .post( "/purposes/:purposeId/versions/:versionId/activate", authorizationMiddleware([ADMIN_ROLE]), From bf7723fff7aa44a3076d68f5d8593e1900e02661 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 12:14:50 +0200 Subject: [PATCH 068/537] Work in progress --- .../src/model/domain/apiConverter.ts | 82 +++++++++++++++++++ .../src/model/domain/models.ts | 12 +++ 2 files changed, 94 insertions(+) create mode 100644 packages/purpose-process/src/model/domain/apiConverter.ts create mode 100644 packages/purpose-process/src/model/domain/models.ts diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts new file mode 100644 index 0000000000..f0a2473698 --- /dev/null +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -0,0 +1,82 @@ +import { match } from "ts-pattern"; +import { + Purpose, + PurposeRiskAnalysisForm, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, +} from "pagopa-interop-models"; +import { + ApiPurpose, + ApiPurposeVersion, + ApiPurposeVersionDocument, + ApiPurposeVersionState, + ApiRiskAnalysisForm, +} from "./models.js"; + +export const riskAnalysisFormToApiRiskAnalysisForm = ( + riskAnalysisForm: PurposeRiskAnalysisForm +): ApiRiskAnalysisForm => ({ + version: riskAnalysisForm.version, + riskAnalysisId: riskAnalysisForm.riskAnalysisId, +}); + +export const purposeVersionDocumentToApiPurposeVersionDocument = ( + document: PurposeVersionDocument +): ApiPurposeVersionDocument => ({ + id: document.id, + contentType: document.contentType, + path: document.path, + createdAt: document.createdAt.toJSON(), +}); + +export const purposeVersionStateToApiPurposeVersionState = ( + state: PurposeVersionState +): ApiPurposeVersionState => + match(state) + .with(purposeVersionState.active, () => "ACTIVE") + .with(purposeVersionState.archived, () => "ARCHIVED") + .with(purposeVersionState.draft, () => "DRAFT") + .with(purposeVersionState.rejected, () => "WAITING_FOR_APPROVAL") // TODO this should be "REJECTED" but it's not in the api spec yet + .with(purposeVersionState.suspended, () => "SUSPENDED") + .with(purposeVersionState.waitingForApproval, () => "WAITING_FOR_APPROVAL") + .exhaustive(); + +export const purposeVersionToApiPurposeVersion = ( + version: PurposeVersion +): ApiPurposeVersion => ({ + id: version.id, + state: purposeVersionStateToApiPurposeVersionState(version.state), + createdAt: version.createdAt.toJSON(), + updatedAt: version.updatedAt?.toJSON(), + firstActivationAt: version.firstActivationAt?.toJSON(), + expectedApprovalDate: version.expectedApprovalDate?.toJSON(), + riskAnalysis: version.riskAnalysis + ? purposeVersionDocumentToApiPurposeVersionDocument(version.riskAnalysis) + : undefined, + dailyCalls: version.dailyCalls, + suspendedAt: version.suspendedAt?.toJSON(), +}); + +export const purposeToApiPurpose = ( + purpose: Purpose, + isRiskAnalysisValid: boolean +): ApiPurpose => ({ + id: purpose.id, + eserviceId: purpose.eserviceId, + consumerId: purpose.consumerId, + versions: purpose.versions.map(purposeVersionToApiPurposeVersion), + suspendedByConsumer: purpose.suspendedByConsumer, + suspendedByProducer: purpose.suspendedByProducer, + title: purpose.title, + description: purpose.description, + riskAnalysisForm: purpose.riskAnalysisForm + ? riskAnalysisFormToApiRiskAnalysisForm(purpose.riskAnalysisForm) + : undefined, + createdAt: purpose.createdAt?.toJSON(), + updatedAt: purpose.updatedAt?.toJSON(), + isRiskAnalysisValid, + isFreeOfCharge: purpose.isFreeOfCharge, + freeOfChargeReason: purpose.freeOfChargeReason, +}); diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts new file mode 100644 index 0000000000..9a97747326 --- /dev/null +++ b/packages/purpose-process/src/model/domain/models.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; +import * as api from "../generated/api.js"; + +export type ApiRiskAnalysisForm = z.infer; +export type ApiPurposeVersion = z.infer; +export type ApiPurpose = z.infer; +export type ApiPurposeVersionState = z.infer< + typeof api.schemas.PurposeVersionState +>; +export type ApiPurposeVersionDocument = z.infer< + typeof api.schemas.PurposeVersionDocument +>; From a53db07d5d01cc428374c70d3072b314112bc1e2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 12:17:51 +0200 Subject: [PATCH 069/537] Use rejected --- packages/purpose-process/src/model/domain/apiConverter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index f0a2473698..7b152a864e 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -38,7 +38,7 @@ export const purposeVersionStateToApiPurposeVersionState = ( .with(purposeVersionState.active, () => "ACTIVE") .with(purposeVersionState.archived, () => "ARCHIVED") .with(purposeVersionState.draft, () => "DRAFT") - .with(purposeVersionState.rejected, () => "WAITING_FOR_APPROVAL") // TODO this should be "REJECTED" but it's not in the api spec yet + .with(purposeVersionState.rejected, () => "REJECTED") .with(purposeVersionState.suspended, () => "SUSPENDED") .with(purposeVersionState.waitingForApproval, () => "WAITING_FOR_APPROVAL") .exhaustive(); From ba9e189e152c08ab1cae57c0033f7608ddb99077 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 14:28:27 +0200 Subject: [PATCH 070/537] Update model --- packages/models/src/risk-analysis/riskAnalysis.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 7970c18847..f1c8251782 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -28,10 +28,12 @@ export const RiskAnalysisForm = z.object({ }); export type RiskAnalysisForm = z.infer; -export const PurposeRiskAnalysisForm = z.object({ - ...RiskAnalysisForm.shape, - riskAnalysisId: RiskAnalysisId.optional(), -}); +export const PurposeRiskAnalysisForm = RiskAnalysisForm.and( + z.object({ + riskAnalysisId: RiskAnalysisId.optional(), + }) +); + export type PurposeRiskAnalysisForm = z.infer; export const RiskAnalysis = z.object({ From 9ab372874e81004c85b5f9e23e5373fcc7951c94 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 15:07:08 +0200 Subject: [PATCH 071/537] Fix --- packages/models/src/purpose/protobufConverterFromV2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index 44620f5786..026802ebda 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -87,7 +87,7 @@ export const fromPurposeV2 = (input: PurposeV2): Purpose => ({ versions: input.versions.map(fromPurposeVersionV2), isFreeOfCharge: input.isFreeOfCharge, createdAt: bigIntToDate(input.createdAt), - updatedAt: bigIntToDateOrUndefined(input.createdAt), + updatedAt: bigIntToDateOrUndefined(input.updatedAt), riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV2(input.riskAnalysisForm) : undefined, From da6787bff5955102a38dd3995ae191f50fca25ca Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 17:20:19 +0200 Subject: [PATCH 072/537] Work in progress --- .../src/model/domain/apiConverter.ts | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index 7b152a864e..b28108165d 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -1,7 +1,6 @@ import { match } from "ts-pattern"; import { Purpose, - PurposeRiskAnalysisForm, PurposeVersion, PurposeVersionDocument, PurposeVersionState, @@ -12,24 +11,42 @@ import { ApiPurposeVersion, ApiPurposeVersionDocument, ApiPurposeVersionState, - ApiRiskAnalysisForm, } from "./models.js"; +/* +export const singleAnswersToApiSingleAnswers = ( + singleAnswers: RiskAnalysisSingleAnswer[] +) => { + const m = singleAnswers.map((a) => [a.key, [a.value]]); + return m; +}; + +export const multiAnswersToApiMultiAnswers = ( + multiAnswers: RiskAnalysisMultiAnswer[] +) => { + const m = multiAnswers.map((a) => [a.key, a.values]); + return m; +}; + export const riskAnalysisFormToApiRiskAnalysisForm = ( riskAnalysisForm: PurposeRiskAnalysisForm -): ApiRiskAnalysisForm => ({ - version: riskAnalysisForm.version, - riskAnalysisId: riskAnalysisForm.riskAnalysisId, -}); - -export const purposeVersionDocumentToApiPurposeVersionDocument = ( - document: PurposeVersionDocument -): ApiPurposeVersionDocument => ({ - id: document.id, - contentType: document.contentType, - path: document.path, - createdAt: document.createdAt.toJSON(), -}); +): ApiRiskAnalysisForm => { + const apiSingleAnswersMap = singleAnswersToApiSingleAnswers( + riskAnalysisForm.singleAnswers + ); + const apiMultiAnswersMap = multiAnswersToApiMultiAnswers( + riskAnalysisForm.multiAnswers + ); + return { + version: riskAnalysisForm.version, + answers: new Map([ + ...apiSingleAnswersMap.entries(), + ...apiMultiAnswersMap.entries(), + ]), + riskAnalysisId: riskAnalysisForm.riskAnalysisId, + }; +}; +*/ export const purposeVersionStateToApiPurposeVersionState = ( state: PurposeVersionState @@ -43,6 +60,15 @@ export const purposeVersionStateToApiPurposeVersionState = ( .with(purposeVersionState.waitingForApproval, () => "WAITING_FOR_APPROVAL") .exhaustive(); +export const purposeVersionDocumentToApiPurposeVersionDocument = ( + document: PurposeVersionDocument +): ApiPurposeVersionDocument => ({ + id: document.id, + contentType: document.contentType, + path: document.path, + createdAt: document.createdAt.toJSON(), +}); + export const purposeVersionToApiPurposeVersion = ( version: PurposeVersion ): ApiPurposeVersion => ({ @@ -71,9 +97,9 @@ export const purposeToApiPurpose = ( suspendedByProducer: purpose.suspendedByProducer, title: purpose.title, description: purpose.description, - riskAnalysisForm: purpose.riskAnalysisForm - ? riskAnalysisFormToApiRiskAnalysisForm(purpose.riskAnalysisForm) - : undefined, + // riskAnalysisForm: purpose.riskAnalysisForm + // ? riskAnalysisFormToApiRiskAnalysisForm(purpose.riskAnalysisForm) + // : undefined, createdAt: purpose.createdAt?.toJSON(), updatedAt: purpose.updatedAt?.toJSON(), isRiskAnalysisValid, From 8b852e882c3168f1cee587610ec610dd1dd7bc34 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 17:53:37 +0200 Subject: [PATCH 073/537] Fix --- .../src/model/domain/apiConverter.ts | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index b28108165d..d3ac70afa5 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -1,9 +1,12 @@ import { match } from "ts-pattern"; import { Purpose, + PurposeRiskAnalysisForm, PurposeVersion, PurposeVersionDocument, PurposeVersionState, + RiskAnalysisMultiAnswer, + RiskAnalysisSingleAnswer, purposeVersionState, } from "pagopa-interop-models"; import { @@ -11,21 +14,26 @@ import { ApiPurposeVersion, ApiPurposeVersionDocument, ApiPurposeVersionState, + ApiRiskAnalysisForm, } from "./models.js"; -/* export const singleAnswersToApiSingleAnswers = ( singleAnswers: RiskAnalysisSingleAnswer[] ) => { - const m = singleAnswers.map((a) => [a.key, [a.value]]); - return m; + return singleAnswers.reduce>((acc, curr) => { + if (!curr.value) return acc; + acc[curr.key] = [curr.value]; + return acc; + }, {}); }; export const multiAnswersToApiMultiAnswers = ( multiAnswers: RiskAnalysisMultiAnswer[] ) => { - const m = multiAnswers.map((a) => [a.key, a.values]); - return m; + return multiAnswers.reduce>((acc, curr) => { + acc[curr.key] = curr.values; + return acc; + }, {}); }; export const riskAnalysisFormToApiRiskAnalysisForm = ( @@ -39,14 +47,10 @@ export const riskAnalysisFormToApiRiskAnalysisForm = ( ); return { version: riskAnalysisForm.version, - answers: new Map([ - ...apiSingleAnswersMap.entries(), - ...apiMultiAnswersMap.entries(), - ]), + answers: { ...apiSingleAnswersMap, ...apiMultiAnswersMap }, riskAnalysisId: riskAnalysisForm.riskAnalysisId, }; }; -*/ export const purposeVersionStateToApiPurposeVersionState = ( state: PurposeVersionState @@ -97,9 +101,9 @@ export const purposeToApiPurpose = ( suspendedByProducer: purpose.suspendedByProducer, title: purpose.title, description: purpose.description, - // riskAnalysisForm: purpose.riskAnalysisForm - // ? riskAnalysisFormToApiRiskAnalysisForm(purpose.riskAnalysisForm) - // : undefined, + riskAnalysisForm: purpose.riskAnalysisForm + ? riskAnalysisFormToApiRiskAnalysisForm(purpose.riskAnalysisForm) + : undefined, createdAt: purpose.createdAt?.toJSON(), updatedAt: purpose.updatedAt?.toJSON(), isRiskAnalysisValid, From f46aa984eb7bc26d0f20cc62600266561062ae43 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 10 Apr 2024 18:09:26 +0200 Subject: [PATCH 074/537] Fix lint --- .../src/model/domain/apiConverter.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index d3ac70afa5..ac3b6f1a97 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -19,22 +19,24 @@ import { export const singleAnswersToApiSingleAnswers = ( singleAnswers: RiskAnalysisSingleAnswer[] -) => { - return singleAnswers.reduce>((acc, curr) => { - if (!curr.value) return acc; +): Record => + singleAnswers.reduce>((acc, curr) => { + if (!curr.value) { + return acc; + } + // eslint-disable-next-line functional/immutable-data acc[curr.key] = [curr.value]; return acc; }, {}); -}; export const multiAnswersToApiMultiAnswers = ( multiAnswers: RiskAnalysisMultiAnswer[] -) => { - return multiAnswers.reduce>((acc, curr) => { +): Record => + multiAnswers.reduce>((acc, curr) => { + // eslint-disable-next-line functional/immutable-data acc[curr.key] = curr.values; return acc; }, {}); -}; export const riskAnalysisFormToApiRiskAnalysisForm = ( riskAnalysisForm: PurposeRiskAnalysisForm From 25574c364d1ce35e54418c01a7e5f993b08be92b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 09:39:02 +0200 Subject: [PATCH 075/537] Init readmodelservice --- .../src/services/readModelService.ts | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 packages/purpose-process/src/services/readModelService.ts diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts new file mode 100644 index 0000000000..7f36e75f16 --- /dev/null +++ b/packages/purpose-process/src/services/readModelService.ts @@ -0,0 +1,146 @@ +import { + logger, + ReadModelRepository, + EServiceCollection, + TenantCollection, + AttributeCollection, + PurposeCollection, +} from "pagopa-interop-commons"; +import { + EService, + genericError, + WithMetadata, + EServiceId, + TenantId, + Tenant, + EServiceReadModel, + Attribute, + AttributeReadmodel, + AttributeId, + Purpose, +} from "pagopa-interop-models"; +import { Filter, WithId } from "mongodb"; +import { z } from "zod"; + +async function getPurpose( + purposes: PurposeCollection, + filter: Filter>> +): Promise | undefined> { + const data = await purposes.findOne(filter, { + projection: { data: true, metadata: true }, + }); + if (!data) { + return undefined; + } else { + const result = z + .object({ + data: Purpose, + metadata: z.object({ version: z.number() }), + }) + .safeParse(data); + if (!result.success) { + logger.error( + `Unable to parse eService item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse eService item"); + } + return result.data; + } +} + +async function getEService( + eservices: EServiceCollection, + filter: Filter>> +): Promise { + const data = await eservices.findOne(filter, { + projection: { data: true, metadata: true }, + }); + if (!data) { + return undefined; + } else { + const result = EService.safeParse(data); + if (!result.success) { + logger.error( + `Unable to parse eService item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse eService item"); + } + return result.data; + } +} + +async function getAttribute( + attributes: AttributeCollection, + filter: Filter>> +): Promise { + const data = await attributes.findOne(filter, { + projection: { data: true, metadata: true }, + }); + if (!data) { + return undefined; + } else { + const result = Attribute.safeParse(data); + if (!result.success) { + logger.error( + `Unable to parse eService item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse eService item"); + } + return result.data; + } +} + +async function getTenant( + tenants: TenantCollection, + filter: Filter>> +): Promise { + const data = await tenants.findOne(filter, { + projection: { data: true, metadata: true }, + }); + if (!data) { + return undefined; + } else { + const result = Tenant.safeParse(data); + if (!result.success) { + logger.error( + `Unable to parse eService item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse eService item"); + } + return result.data; + } +} + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function readModelServiceBuilder( + readModelRepository: ReadModelRepository +) { + const { attributes, eservices, purposes, tenants } = readModelRepository; + + return { + async getEServiceById(id: EServiceId): Promise { + return getEService(eservices, { "data.id": id }); + }, + async getAttributeById(id: AttributeId): Promise { + return getAttribute(attributes, { "data.id": id }); + }, + async getTenantById(id: TenantId): Promise { + return getTenant(tenants, { "data.id": id }); + }, + async getPurposeById( + id: TenantId + ): Promise | undefined> { + return getPurpose(purposes, { "data.id": id }); + }, + }; +} + +export type ReadModelService = ReturnType; From d2d6b909d121c4758321655eaec5fa36bbe2d546 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 09:52:37 +0200 Subject: [PATCH 076/537] Fix id type --- packages/purpose-process/src/services/readModelService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 7f36e75f16..99a73648d8 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -18,6 +18,7 @@ import { AttributeReadmodel, AttributeId, Purpose, + PurposeId, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -136,7 +137,7 @@ export function readModelServiceBuilder( return getTenant(tenants, { "data.id": id }); }, async getPurposeById( - id: TenantId + id: PurposeId ): Promise | undefined> { return getPurpose(purposes, { "data.id": id }); }, From 2ac0c90e3c54cadba68fcd5199b7aaf0f003b73c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 09:54:53 +0200 Subject: [PATCH 077/537] Remove not needed function --- .../src/services/readModelService.ts | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 99a73648d8..b42e677710 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -3,7 +3,6 @@ import { ReadModelRepository, EServiceCollection, TenantCollection, - AttributeCollection, PurposeCollection, } from "pagopa-interop-commons"; import { @@ -14,9 +13,6 @@ import { TenantId, Tenant, EServiceReadModel, - Attribute, - AttributeReadmodel, - AttributeId, Purpose, PurposeId, } from "pagopa-interop-models"; @@ -74,29 +70,6 @@ async function getEService( } } -async function getAttribute( - attributes: AttributeCollection, - filter: Filter>> -): Promise { - const data = await attributes.findOne(filter, { - projection: { data: true, metadata: true }, - }); - if (!data) { - return undefined; - } else { - const result = Attribute.safeParse(data); - if (!result.success) { - logger.error( - `Unable to parse eService item: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - throw genericError("Unable to parse eService item"); - } - return result.data; - } -} - async function getTenant( tenants: TenantCollection, filter: Filter>> @@ -124,15 +97,12 @@ async function getTenant( export function readModelServiceBuilder( readModelRepository: ReadModelRepository ) { - const { attributes, eservices, purposes, tenants } = readModelRepository; + const { eservices, purposes, tenants } = readModelRepository; return { async getEServiceById(id: EServiceId): Promise { return getEService(eservices, { "data.id": id }); }, - async getAttributeById(id: AttributeId): Promise { - return getAttribute(attributes, { "data.id": id }); - }, async getTenantById(id: TenantId): Promise { return getTenant(tenants, { "data.id": id }); }, From 37dbbe5364e9eab7e94fe70e854bb9551c286d1d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 09:58:12 +0200 Subject: [PATCH 078/537] Add scaffold for errors --- .../src/model/domain/errors.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/purpose-process/src/model/domain/errors.ts diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts new file mode 100644 index 0000000000..6d1ff8ffe3 --- /dev/null +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -0,0 +1,44 @@ +import { + ApiError, + EServiceId, + PurposeId, + TenantId, + makeApiProblemBuilder, +} from "pagopa-interop-models"; +import { logger } from "pagopa-interop-commons"; + +export const errorCodes = { + purposeNotFound: "0001", + eserviceNotFound: "0002", + tenantNotFound: "0003", +}; + +export type ErrorCodes = keyof typeof errorCodes; + +export const makeApiProblem = makeApiProblemBuilder(logger, errorCodes); + +export function purposeNotFoundFound( + purposeId: PurposeId +): ApiError { + return new ApiError({ + detail: `Purpose ${purposeId} not found`, + code: "purposeNotFound", + title: "Purpose not found", + }); +} + +export function eserviceNotFound(eserviceId: EServiceId): ApiError { + return new ApiError({ + detail: `EService ${eserviceId} not found`, + code: "eserviceNotFound", + title: "EService not found", + }); +} + +export function tenantNotFound(tenantId: TenantId): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} not found`, + code: "tenantNotFound", + title: "Tenant not found", + }); +} From 8f6170fd3b4551fb246ec262622aa1400587940b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 10:48:49 +0200 Subject: [PATCH 079/537] Fix typo --- packages/purpose-process/src/model/domain/errors.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 6d1ff8ffe3..a4a85acbb7 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -17,9 +17,7 @@ export type ErrorCodes = keyof typeof errorCodes; export const makeApiProblem = makeApiProblemBuilder(logger, errorCodes); -export function purposeNotFoundFound( - purposeId: PurposeId -): ApiError { +export function purposeNotFound(purposeId: PurposeId): ApiError { return new ApiError({ detail: `Purpose ${purposeId} not found`, code: "purposeNotFound", From f6d9aefc46471717e82f1fb90e91f5398bd4c8e3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 11:25:47 +0200 Subject: [PATCH 080/537] Add purposeService with getPurposeById --- .../src/services/purposeService.ts | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 packages/purpose-process/src/services/purposeService.ts diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts new file mode 100644 index 0000000000..c915c9eef5 --- /dev/null +++ b/packages/purpose-process/src/services/purposeService.ts @@ -0,0 +1,152 @@ +import { + AuthData, + DB, + eventRepository, + logger, + riskAnalysisFormToRiskAnalysisFormToValidate, + validateRiskAnalysis, +} from "pagopa-interop-commons"; +import { + EService, + EServiceId, + TenantId, + WithMetadata, + Tenant, + Purpose, + PurposeId, + purposeEventToBinaryData, + TenantKind, + purposeVersionState, + RiskAnalysisForm, +} from "pagopa-interop-models"; +import { + eserviceNotFound, + purposeNotFound, + tenantNotFound, +} from "../model/domain/errors.js"; +import { ReadModelService } from "./readModelService.js"; + +const retrievePurpose = async ( + purposeId: PurposeId, + readModelService: ReadModelService +): Promise> => { + const purpose = await readModelService.getPurposeById(purposeId); + if (purpose === undefined) { + throw purposeNotFound(purposeId); + } + return purpose; +}; + +const retrieveEService = async ( + eserviceId: EServiceId, + readModelService: ReadModelService +): Promise => { + const eservice = await readModelService.getEServiceById(eserviceId); + if (eservice === undefined) { + throw eserviceNotFound(eserviceId); + } + return eservice; +}; + +const retrieveTenant = async ( + tenantId: TenantId, + readModelService: ReadModelService +): Promise => { + const tenant = await readModelService.getTenantById(tenantId); + if (tenant === undefined) { + throw tenantNotFound(tenantId); + } + return tenant; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function purposeServiceBuilder( + dbInstance: DB, + readModelService: ReadModelService +) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const repository = eventRepository(dbInstance, purposeEventToBinaryData); + + return { + async getPurposeById( + purposeId: PurposeId, + authData: AuthData + ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + logger.info(`Retrieving Purpose ${purposeId}`); + + const purpose = await retrievePurpose(purposeId, readModelService); + const eservice = await retrieveEService( + purpose.data.eserviceId, + readModelService + ); + const tenant = await retrieveTenant( + authData.organizationId, + readModelService + ); + + if (tenant.kind === undefined) { + throw new Error(); + } + + return authorizeRiskAnalysisForm({ + purpose: purpose.data, + producerId: eservice.producerId, + organizationId: authData.organizationId, + tenantKind: tenant.kind, + }); + }, + }; +} + +export type PurposeService = ReturnType; + +const authorizeRiskAnalysisForm = ({ + purpose, + producerId, + organizationId, + tenantKind, +}: { + purpose: Purpose; + producerId: TenantId; + organizationId: TenantId; + tenantKind: TenantKind; +}): { purpose: Purpose; isRiskAnalysisValid: boolean } => { + if (organizationId === purpose.consumerId || organizationId === producerId) { + if (purposeIsDraft(purpose)) { + const isRiskAnalysisValid = isRiskAnalysisFormValid( + purpose.riskAnalysisForm, + false, + tenantKind + ); + return { purpose, isRiskAnalysisValid }; + } else { + return { purpose, isRiskAnalysisValid: true }; + } + } else { + return { + purpose: { ...purpose, riskAnalysisForm: undefined }, + isRiskAnalysisValid: false, + }; + } +}; + +const isRiskAnalysisFormValid = ( + riskAnalysisForm: RiskAnalysisForm | undefined, + schemaOnlyValidation: boolean, + tenantKind: TenantKind +): boolean => { + if (riskAnalysisForm === undefined) { + return false; + } else { + return ( + validateRiskAnalysis( + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), + schemaOnlyValidation, + tenantKind + ).type === "valid" + ); + } +}; + +const purposeIsDraft = (purpose: Purpose): boolean => + !purpose.versions.some((v) => v.state !== purposeVersionState.draft); From ed8268425b9f6d42bf2d55715c62165926cd3345 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 11:26:06 +0200 Subject: [PATCH 081/537] Update test --- .../test/purposeService.integration.test.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 6baca7fcbf..dec34fac78 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -19,11 +19,19 @@ import { } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; import { config } from "../src/utilities/config.js"; +import { + PurposeService, + purposeServiceBuilder, +} from "../src/services/purposeService.js"; +import { + ReadModelService, + readModelServiceBuilder, +} from "../src/services/readModelService.js"; describe("database test", async () => { let purposes: PurposeCollection; - // let readModelService: ReadModelService; - // let purposeService: PurposeService; + let readModelService: ReadModelService; + let purposeService: PurposeService; let postgresDB: IDatabase; let startedPostgreSqlContainer: StartedTestContainer; let startedMongodbContainer: StartedTestContainer; @@ -40,7 +48,7 @@ describe("database test", async () => { const readModelRepository = ReadModelRepository.init(config); purposes = readModelRepository.purposes; - // readModelService = readModelServiceBuilder(readModelRepository); + readModelService = readModelServiceBuilder(readModelRepository); postgresDB = initDB({ username: config.eventStoreDbUsername, password: config.eventStoreDbPassword, @@ -50,7 +58,7 @@ describe("database test", async () => { schema: config.eventStoreDbSchema, useSSL: config.eventStoreDbUseSSL, }); - // purposeService = purposeServiceBuilder(postgresDB, readModelService); + purposeService = purposeServiceBuilder(postgresDB, readModelService); }); afterEach(async () => { @@ -65,7 +73,7 @@ describe("database test", async () => { }); describe("Purpose service", () => { - describe("create purpose", () => { + describe("getPurposeById", () => { it("TO DO", () => { expect(1).toBe(1); }); From f81f2ffe71eeb668a197f6fb1c6c9d6d6b9b3273 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 11:36:01 +0200 Subject: [PATCH 082/537] Add error mapper --- .../purpose-process/src/utilities/errorMappers.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 packages/purpose-process/src/utilities/errorMappers.ts diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts new file mode 100644 index 0000000000..1be556ade6 --- /dev/null +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -0,0 +1,15 @@ +import { constants } from "http2"; +import { ApiError, CommonErrorCodes } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { ErrorCodes as LocalErrorCodes } from "../model/domain/errors.js"; + +type ErrorCodes = LocalErrorCodes | CommonErrorCodes; + +const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_NOT_FOUND } = constants; + +export const getPurposeErrorMapper = (error: ApiError): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 1532715d59874656159571855dc62b0836241058 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 11:36:19 +0200 Subject: [PATCH 083/537] Add route and adjust api spec --- .../open-api/purpose-service-spec.yml | 2 +- .../src/routers/PurposeRouter.ts | 43 ++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index bce802f136..4deadcf3e5 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -256,7 +256,7 @@ paths: '404': description: Purpose Not Found content: - application/problem+json: + application/json: schema: $ref: '#/components/schemas/Problem' post: diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 20bfebc724..ee3bf8e0f0 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -5,8 +5,34 @@ import { userRoles, ZodiosContext, authorizationMiddleware, + ReadModelRepository, + initDB, } from "pagopa-interop-commons"; +import { unsafeBrandId } from "pagopa-interop-models"; import { api } from "../model/generated/api.js"; +import { purposeToApiPurpose } from "../model/domain/apiConverter.js"; +import { readModelServiceBuilder } from "../services/readModelService.js"; +import { config } from "../utilities/config.js"; +import { purposeServiceBuilder } from "../services/purposeService.js"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { getPurposeErrorMapper } from "../utilities/errorMappers.js"; + +const readModelService = readModelServiceBuilder( + ReadModelRepository.init(config) +); + +const purposeService = purposeServiceBuilder( + initDB({ + username: config.eventStoreDbUsername, + password: config.eventStoreDbPassword, + host: config.eventStoreDbHost, + port: config.eventStoreDbPort, + database: config.eventStoreDbName, + schema: config.eventStoreDbSchema, + useSSL: config.eventStoreDbUseSSL, + }), + readModelService +); const purposeRouter = ( ctx: ZodiosContext @@ -55,7 +81,22 @@ const purposeRouter = ( M2M_ROLE, SUPPORT_ROLE, ]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.getPurposeById( + unsafeBrandId(req.params.id), + req.ctx.authData + ); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, getPurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post("/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => res.status(501).send() From 89b8b1a3a98d6f4e53f28113a1bb3cf09754969e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 11:39:46 +0200 Subject: [PATCH 084/537] Fix build and lint --- packages/purpose-process/src/services/purposeService.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c915c9eef5..c05445e048 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,7 +1,6 @@ import { AuthData, DB, - eventRepository, logger, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, @@ -14,7 +13,6 @@ import { Tenant, Purpose, PurposeId, - purposeEventToBinaryData, TenantKind, purposeVersionState, RiskAnalysisForm, @@ -61,11 +59,10 @@ const retrieveTenant = async ( // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( - dbInstance: DB, + _dbInstance: DB, readModelService: ReadModelService ) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const repository = eventRepository(dbInstance, purposeEventToBinaryData); + // const repository = eventRepository(dbInstance, purposeEventToBinaryData); return { async getPurposeById( From 830ce957bd895f7a3d1c8b838620b2397c2a7f52 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 11:52:13 +0200 Subject: [PATCH 085/537] Add error --- packages/purpose-process/src/model/domain/errors.ts | 9 +++++++++ packages/purpose-process/src/services/purposeService.ts | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a4a85acbb7..3ea75c6775 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -11,6 +11,7 @@ export const errorCodes = { purposeNotFound: "0001", eserviceNotFound: "0002", tenantNotFound: "0003", + tenantKindNotFound: "0004", }; export type ErrorCodes = keyof typeof errorCodes; @@ -40,3 +41,11 @@ export function tenantNotFound(tenantId: TenantId): ApiError { title: "Tenant not found", }); } + +export function tenantKindNotFound(tenantId: TenantId): ApiError { + return new ApiError({ + detail: `Tenant kind for tenant ${tenantId} not found`, + code: "tenantKindNotFound", + title: "Tenant kind not found", + }); +} diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c05445e048..87086f427a 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -20,6 +20,7 @@ import { import { eserviceNotFound, purposeNotFound, + tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -82,7 +83,7 @@ export function purposeServiceBuilder( ); if (tenant.kind === undefined) { - throw new Error(); + throw tenantKindNotFound(tenant.id); } return authorizeRiskAnalysisForm({ From 38be054d3a62410a60c1fe71ebeaa48921a1cbb0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 12:27:24 +0200 Subject: [PATCH 086/537] Update commons-test --- .../commons-test/src/eventStoreTestUtils.ts | 1 + packages/commons-test/src/testUtils.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/commons-test/src/eventStoreTestUtils.ts b/packages/commons-test/src/eventStoreTestUtils.ts index 5b772aa652..8e0d446cbe 100644 --- a/packages/commons-test/src/eventStoreTestUtils.ts +++ b/packages/commons-test/src/eventStoreTestUtils.ts @@ -22,6 +22,7 @@ export const eventStoreSchema = { attribute: "attribute", catalog: "catalog", tenant: "tenant", + purpose: "purpose", } as const; export const EventStoreSchema = z.enum([ diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 00c950ab2a..7975b5f560 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -171,3 +171,26 @@ export const getMockPurposeVersion = (): PurposeVersion => ({ dailyCalls: 10, createdAt: new Date(), }); + +export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ + organizationId: organizationId || generateId(), + userId: uuidv4(), + userRoles: [], + externalId: { + value: "123456", + origin: "IPA", + }, +}); + +export const getMockTenant = (): Tenant => ({ + name: "A tenant", + id: generateId(), + createdAt: new Date(), + attributes: [], + externalId: { + value: "123456", + origin: "IPA", + }, + features: [], + mails: [], +}); From 3b58476762c90eb7038d14ad38b1b10562e0d000 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 12:27:30 +0200 Subject: [PATCH 087/537] Add missing export --- packages/models/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 43b015c4a4..c68dd8461d 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -33,6 +33,8 @@ export * from "./purpose/purpose.js"; export * from "./purpose/purposeEvents.js"; export * from "./purpose/protobufConverterFromV1.js"; export * from "./purpose/protobufConverterToV1.js"; +export * from "./purpose/protobufConverterFromV2.js"; +export * from "./purpose/protobufConverterToV2.js"; export * from "./user/user.js"; From 3062b41a48593dc6eb2269bd2016fc09b4018995 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 12:27:43 +0200 Subject: [PATCH 088/537] Fix parsing --- packages/purpose-process/src/services/readModelService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index b42e677710..af22c02358 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -52,12 +52,12 @@ async function getEService( filter: Filter>> ): Promise { const data = await eservices.findOne(filter, { - projection: { data: true, metadata: true }, + projection: { data: true }, }); if (!data) { return undefined; } else { - const result = EService.safeParse(data); + const result = EService.safeParse(data.data); if (!result.success) { logger.error( `Unable to parse eService item: result ${JSON.stringify( @@ -75,12 +75,12 @@ async function getTenant( filter: Filter>> ): Promise { const data = await tenants.findOne(filter, { - projection: { data: true, metadata: true }, + projection: { data: true }, }); if (!data) { return undefined; } else { - const result = Tenant.safeParse(data); + const result = Tenant.safeParse(data.data); if (!result.success) { logger.error( `Unable to parse eService item: result ${JSON.stringify( From 0efe45ec8cec4e5fda244327171ab380f540fae9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 12:27:52 +0200 Subject: [PATCH 089/537] Implement tests --- .../test/purposeService.integration.test.ts | 68 ++++++++++++++++++- packages/purpose-process/test/utils.ts | 58 ++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 packages/purpose-process/test/utils.ts diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index dec34fac78..077cd0e7bc 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -5,8 +5,10 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; import { + EServiceCollection, PurposeCollection, ReadModelRepository, + TenantCollection, initDB, } from "pagopa-interop-commons"; import { IDatabase } from "pg-promise"; @@ -14,10 +16,23 @@ import { IDatabase } from "pg-promise"; import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, + getMockAuthData, + getMockPurpose, + getMockTenant, mongoDBContainer, postgreSQLContainer, + writeInReadmodel, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; +import { + EService, + Purpose, + PurposeId, + TenantKind, + generateId, + tenantKind, + toReadModelEService, +} from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { PurposeService, @@ -27,9 +42,13 @@ import { ReadModelService, readModelServiceBuilder, } from "../src/services/readModelService.js"; +import { purposeNotFound } from "../src/model/domain/errors.js"; +import { addOnePurpose, getMockEService } from "./utils.js"; describe("database test", async () => { let purposes: PurposeCollection; + let eservices: EServiceCollection; + let tenants: TenantCollection; let readModelService: ReadModelService; let purposeService: PurposeService; let postgresDB: IDatabase; @@ -48,6 +67,8 @@ describe("database test", async () => { const readModelRepository = ReadModelRepository.init(config); purposes = readModelRepository.purposes; + eservices = readModelRepository.eservices; + tenants = readModelRepository.tenants; readModelService = readModelServiceBuilder(readModelRepository); postgresDB = initDB({ username: config.eventStoreDbUsername, @@ -73,8 +94,53 @@ describe("database test", async () => { }); describe("Purpose service", () => { + const mockPurpose = getMockPurpose(); describe("getPurposeById", () => { - it("TO DO", () => { + it("Should get the purpose if it exists", async () => { + const mockEService = getMockEService(); + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(mockTenant, tenants); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + getMockAuthData(mockTenant.id) + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: false, + }); + }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + const notExistingId: PurposeId = generateId(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.getPurposeById(notExistingId, getMockAuthData()) + ).rejects.toThrowError(purposeNotFound(notExistingId)); + }); + it("Should throw eserviceNotFound if the eservice doesn't exist", () => { + expect(1).toBe(1); + }); + it("Should throw tenantNotFound if the tenant doesn't exist", () => { + expect(1).toBe(1); + }); + it("Should throw tenantKindNotFound if the tenant doesn't exist", () => { expect(1).toBe(1); }); }); diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts new file mode 100644 index 0000000000..885b604305 --- /dev/null +++ b/packages/purpose-process/test/utils.ts @@ -0,0 +1,58 @@ +import { PurposeCollection } from "pagopa-interop-commons"; +import { + writeInEventstore, + writeInReadmodel, +} from "pagopa-interop-commons-test"; +import { + EService, + Purpose, + PurposeEvent, + generateId, + purposeEventToBinaryData, + technology, + toPurposeV2, +} from "pagopa-interop-models"; +import { IDatabase } from "pg-promise"; + +export const addOnePurpose = async ( + purpose: Purpose, + postgresDB: IDatabase, + purposes: PurposeCollection +): Promise => { + await writePurposeInEventstore(purpose, postgresDB); + await writeInReadmodel(purpose, purposes); +}; + +export const writePurposeInEventstore = async ( + purpose: Purpose, + postgresDB: IDatabase +): Promise => { + const purposeEvent: PurposeEvent = { + type: "PurposeAdded", + event_version: 2, + data: { purpose: toPurposeV2(purpose) }, + }; + const eventToWrite = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stream_id: purposeEvent.data.purpose!.id, + version: "0", + type: purposeEvent.type, + event_version: purposeEvent.event_version, + data: purposeEventToBinaryData(purposeEvent), + }; + + await writeInEventstore(eventToWrite, "purpose", postgresDB); +}; + +export const getMockEService = (): EService => ({ + id: generateId(), + name: "eService name", + description: "eService description", + createdAt: new Date(), + producerId: generateId(), + technology: technology.rest, + descriptors: [], + attributes: undefined, + riskAnalysis: [], + mode: "Deliver", +}); From 1bda4f802d9170e30a47bc14d8bc4ebb0fe11449 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 12:29:11 +0200 Subject: [PATCH 090/537] Add test --- .../test/purposeService.integration.test.ts | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 077cd0e7bc..24879bec3c 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -42,7 +42,10 @@ import { ReadModelService, readModelServiceBuilder, } from "../src/services/readModelService.js"; -import { purposeNotFound } from "../src/model/domain/errors.js"; +import { + purposeNotFound, + tenantKindNotFound, +} from "../src/model/domain/errors.js"; import { addOnePurpose, getMockEService } from "./utils.js"; describe("database test", async () => { @@ -140,8 +143,30 @@ describe("database test", async () => { it("Should throw tenantNotFound if the tenant doesn't exist", () => { expect(1).toBe(1); }); - it("Should throw tenantKindNotFound if the tenant doesn't exist", () => { - expect(1).toBe(1); + it("Should throw tenantKindNotFound if the tenant doesn't exist", async () => { + const mockEService = getMockEService(); + const mockTenant = getMockTenant(); + + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.getPurposeById( + mockPurpose1.id, + getMockAuthData(mockTenant.id) + ) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); }); }); From 4a7a48abb727f2e7b286eca8d3009151ed30c7fb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 15:48:29 +0200 Subject: [PATCH 091/537] Add errors --- .../src/model/domain/errors.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 3ea75c6775..0e0c4b47cd 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -2,6 +2,8 @@ import { ApiError, EServiceId, PurposeId, + PurposeVersionDocumentId, + PurposeVersionId, TenantId, makeApiProblemBuilder, } from "pagopa-interop-models"; @@ -12,6 +14,9 @@ export const errorCodes = { eserviceNotFound: "0002", tenantNotFound: "0003", tenantKindNotFound: "0004", + purposeVersionNotFound: "0005", + purposeVersionDocumentNotFound: "0006", + organizationNotAllowed: "0007", }; export type ErrorCodes = keyof typeof errorCodes; @@ -49,3 +54,36 @@ export function tenantKindNotFound(tenantId: TenantId): ApiError { title: "Tenant kind not found", }); } + +export function purposeVersionNotFound( + purposeId: PurposeId, + versionId: PurposeVersionId +): ApiError { + return new ApiError({ + detail: `Version ${versionId} not found for purpose ${purposeId}`, + code: "purposeVersionNotFound", + title: "Purpose version not found", + }); +} + +export function purposeVersionDocumentNotFound( + purposeId: PurposeId, + versionId: PurposeVersionId, + documentId: PurposeVersionDocumentId +): ApiError { + return new ApiError({ + detail: `Document ${documentId} not found for version ${versionId} of purpose ${purposeId}`, + code: "purposeVersionDocumentNotFound", + title: "Purpose version document not found", + }); +} + +export function organizationNotAllowed( + organizationId: TenantId +): ApiError { + return new ApiError({ + detail: `Organization ${organizationId} is not allowed to perform the operation`, + code: "organizationNotAllowed", + title: "Organization not allowed", + }); +} From 2ce47359bbd9a2b79fe5c9e948eaa176402a27dc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 15:48:35 +0200 Subject: [PATCH 092/537] Add error mapper --- .../purpose-process/src/utilities/errorMappers.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 1be556ade6..2421b17f36 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -5,7 +5,11 @@ import { ErrorCodes as LocalErrorCodes } from "../model/domain/errors.js"; type ErrorCodes = LocalErrorCodes | CommonErrorCodes; -const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_NOT_FOUND } = constants; +const { + HTTP_STATUS_INTERNAL_SERVER_ERROR, + HTTP_STATUS_NOT_FOUND, + HTTP_STATUS_FORBIDDEN, +} = constants; export const getPurposeErrorMapper = (error: ApiError): number => match(error.code) @@ -13,3 +17,12 @@ export const getPurposeErrorMapper = (error: ApiError): number => .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getRiskAnalysisDocumentErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 2ab798d0d7a891f68dd6b2e639e9692f8e84980f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 15:48:48 +0200 Subject: [PATCH 093/537] Update api spec --- packages/purpose-process/open-api/purpose-service-spec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index 4deadcf3e5..11295e6955 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -428,7 +428,7 @@ paths: '400': description: Bad request content: - application/problem+json: + application/json: schema: $ref: '#/components/schemas/Problem' /status: From f74e830af51372df7602fbb80d8471f238e5459e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 15:49:01 +0200 Subject: [PATCH 094/537] Add ownership enum --- packages/models/src/purpose/purpose.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index d90a9153a4..9190128be2 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -60,3 +60,14 @@ export const Purpose = z.object({ freeOfChargeReason: z.string().optional(), }); export type Purpose = z.infer; + +export const ownership = { + CONSUMER: "CONSUMER", + PRODUCER: "PRODUCER", + SELF_CONSUMER: "SELF_CONSUMER", +} as const; +export const Ownership = z.enum([ + Object.values(ownership)[0], + ...Object.values(ownership).slice(1), +]); +export type Ownership = z.infer; From cf5d0efbf5f79fbf830afd7d6c676388b15d6b3f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 15:50:19 +0200 Subject: [PATCH 095/537] Implement endpoint --- .../src/services/purposeService.ts | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 87086f427a..c814605200 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -16,10 +16,18 @@ import { TenantKind, purposeVersionState, RiskAnalysisForm, + PurposeVersionId, + PurposeVersionDocumentId, + PurposeVersion, + PurposeVersionDocument, + ownership, } from "pagopa-interop-models"; import { eserviceNotFound, + organizationNotAllowed, purposeNotFound, + purposeVersionDocumentNotFound, + purposeVersionNotFound, tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; @@ -36,6 +44,39 @@ const retrievePurpose = async ( return purpose; }; +const retrievePurposeVersion = ( + versionId: PurposeVersionId, + purpose: WithMetadata +): PurposeVersion => { + const version = purpose.data.versions.find( + (v: PurposeVersion) => v.id === versionId + ); + + if (version === undefined) { + throw purposeVersionNotFound(purpose.data.id, versionId); + } + + return version; +}; + +const retrievePurposeVersionDocument = ( + purposeId: PurposeId, + purposeVersion: PurposeVersion, + documentId: PurposeVersionDocumentId +): PurposeVersionDocument => { + const document = purposeVersion.riskAnalysis; + + if (document === undefined) { + throw purposeVersionDocumentNotFound( + purposeId, + purposeVersion.id, + documentId + ); + } + + return document; +}; + const retrieveEService = async ( eserviceId: EServiceId, readModelService: ReadModelService @@ -93,6 +134,31 @@ export function purposeServiceBuilder( tenantKind: tenant.kind, }); }, + async getRiskAnalysisDocument({ + purposeId, + versionId, + documentId, + authData, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + documentId: PurposeVersionDocumentId; + authData: AuthData; + }): Promise { + const purpose = await retrievePurpose(purposeId, readModelService); + const eservice = await retrieveEService( + purpose.data.eserviceId, + readModelService + ); + getOrganizationRole({ + organizationId: authData.organizationId, + producerId: eservice.producerId, + consumerId: purpose.data.consumerId, + }); + const version = retrievePurposeVersion(versionId, purpose); + + return retrievePurposeVersionDocument(purposeId, version, documentId); + }, }; } @@ -148,3 +214,23 @@ const isRiskAnalysisFormValid = ( const purposeIsDraft = (purpose: Purpose): boolean => !purpose.versions.some((v) => v.state !== purposeVersionState.draft); + +const getOrganizationRole = ({ + organizationId, + producerId, + consumerId, +}: { + organizationId: TenantId; + producerId: TenantId; + consumerId: TenantId; +}): string => { + if (producerId === consumerId && organizationId === producerId) { + return ownership.SELF_CONSUMER; + } else if (producerId !== consumerId && organizationId === consumerId) { + return ownership.CONSUMER; + } else if (producerId !== consumerId && organizationId === producerId) { + return ownership.PRODUCER; + } else { + throw organizationNotAllowed(organizationId); + } +}; From 98f9e51a7394fafb79d48825ac5d72005e2cf455 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 15:51:02 +0200 Subject: [PATCH 096/537] Update router --- .../src/routers/PurposeRouter.ts | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index ee3bf8e0f0..6c9a7020f3 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -10,12 +10,18 @@ import { } from "pagopa-interop-commons"; import { unsafeBrandId } from "pagopa-interop-models"; import { api } from "../model/generated/api.js"; -import { purposeToApiPurpose } from "../model/domain/apiConverter.js"; +import { + purposeToApiPurpose, + purposeVersionDocumentToApiPurposeVersionDocument, +} from "../model/domain/apiConverter.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../utilities/config.js"; import { purposeServiceBuilder } from "../services/purposeService.js"; import { makeApiProblem } from "../model/domain/errors.js"; -import { getPurposeErrorMapper } from "../utilities/errorMappers.js"; +import { + getPurposeErrorMapper, + getRiskAnalysisDocumentErrorMapper, +} from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( ReadModelRepository.init(config) @@ -119,7 +125,26 @@ const purposeRouter = ( .get( "/purposes/:purposeId/versions/:versionId/documents/:documentId", authorizationMiddleware([ADMIN_ROLE, SUPPORT_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const document = await purposeService.getRiskAnalysisDocument({ + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + documentId: unsafeBrandId(req.params.documentId), + authData: req.ctx.authData, + }); + return res + .status(200) + .json(purposeVersionDocumentToApiPurposeVersionDocument(document)) + .end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + getRiskAnalysisDocumentErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/purposes/:purposeId/versions/:versionId/reject", From 98323fd7a274d6fd83ccb71bd14234c97da4f48d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 17:48:46 +0200 Subject: [PATCH 097/537] Update api spec --- packages/purpose-process/open-api/purpose-service-spec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index 11295e6955..5744241fc7 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -377,7 +377,7 @@ paths: '404': description: Purpose Not Found content: - application/problem+json: + application/json: schema: $ref: '#/components/schemas/Problem' tags: From 014e97db10465cac23529411d31130e0e8e5ed70 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 17:48:52 +0200 Subject: [PATCH 098/537] Add errors --- .../src/model/domain/errors.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 0e0c4b47cd..a9e5f5d66f 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -17,6 +17,8 @@ export const errorCodes = { purposeVersionNotFound: "0005", purposeVersionDocumentNotFound: "0006", organizationNotAllowed: "0007", + organizationIsNotTheConsumer: "0008", + purposeVersionCannotBeDeleted: "0009", }; export type ErrorCodes = keyof typeof errorCodes; @@ -87,3 +89,24 @@ export function organizationNotAllowed( title: "Organization not allowed", }); } + +export function organizationIsNotTheConsumer( + organizationId: TenantId +): ApiError { + return new ApiError({ + detail: `Organization ${organizationId} is not allowed to perform the operation`, + code: "organizationIsNotTheConsumer", + title: "Organization not allowed", + }); +} + +export function purposeVersionCannotBeDeleted( + purposeId: PurposeId, + versionId: PurposeVersionId +): ApiError { + return new ApiError({ + detail: `Version ${versionId} of Purpose ${purposeId} cannot be deleted`, + code: "purposeVersionCannotBeDeleted", + title: "Organization not allowed", + }); +} From 821f843842ae91e0430a81ec0446a49b9b00ebe3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 17:48:59 +0200 Subject: [PATCH 099/537] Add event wrapper --- .../src/model/domain/toEvent.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 packages/purpose-process/src/model/domain/toEvent.ts diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts new file mode 100644 index 0000000000..11632d9fce --- /dev/null +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -0,0 +1,28 @@ +import { CreateEvent } from "pagopa-interop-commons"; +import { + Purpose, + PurposeEvent, + PurposeVersionId, + toPurposeV2, +} from "pagopa-interop-models"; + +export const toCreateEventWaitingForApprovalPurposeVersionDeleted = ({ + purpose, + version, + versionId, + correlationId, +}: { + purpose: Purpose; + version: number; + versionId: PurposeVersionId; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "WaitingForApprovalPurposeVersionDeleted", + event_version: 2, + data: { purpose: toPurposeV2(purpose), versionId }, + }, + correlationId, +}); From fd6ddf4109d1479263012c839d37fc3967c63192 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 17:49:06 +0200 Subject: [PATCH 100/537] Add error mapper --- .../purpose-process/src/utilities/errorMappers.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 2421b17f36..b344b78fe3 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -9,6 +9,7 @@ const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_NOT_FOUND, HTTP_STATUS_FORBIDDEN, + HTTP_STATUS_CONFLICT, } = constants; export const getPurposeErrorMapper = (error: ApiError): number => @@ -26,3 +27,13 @@ export const getRiskAnalysisDocumentErrorMapper = ( .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const deletePurposeVersionErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with("purposeVersionCannotBeDeleted", () => HTTP_STATUS_CONFLICT) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From c2a460b57c9053b10999c1e7938467a439bee86c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 17:49:25 +0200 Subject: [PATCH 101/537] Implement endpoint --- .../src/services/purposeService.ts | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c814605200..8f55067170 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,6 +1,7 @@ import { AuthData, DB, + eventRepository, logger, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, @@ -21,16 +22,20 @@ import { PurposeVersion, PurposeVersionDocument, ownership, + purposeEventToBinaryData, } from "pagopa-interop-models"; import { eserviceNotFound, + organizationIsNotTheConsumer, organizationNotAllowed, purposeNotFound, + purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; +import { toCreateEventWaitingForApprovalPurposeVersionDeleted } from "../model/domain/toEvent.js"; import { ReadModelService } from "./readModelService.js"; const retrievePurpose = async ( @@ -101,10 +106,10 @@ const retrieveTenant = async ( // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( - _dbInstance: DB, + dbInstance: DB, readModelService: ReadModelService ) { - // const repository = eventRepository(dbInstance, purposeEventToBinaryData); + const repository = eventRepository(dbInstance, purposeEventToBinaryData); return { async getPurposeById( @@ -159,6 +164,46 @@ export function purposeServiceBuilder( return retrievePurposeVersionDocument(purposeId, version, documentId); }, + async deletePurposeVersion({ + purposeId, + versionId, + authData, + correlationId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + authData: AuthData; + correlationId: string; + }): Promise { + logger.info(`Deleting Version ${versionId} in Purpose ${purposeId}`); + + const purpose = await retrievePurpose(purposeId, readModelService); + + if (authData.organizationId !== purpose.data.consumerId) { + throw organizationIsNotTheConsumer(authData.organizationId); + } + + const purposeVersion = retrievePurposeVersion(versionId, purpose); + + if (purposeVersion.state !== purposeVersionState.waitingForApproval) { + throw purposeVersionCannotBeDeleted(purposeId, versionId); + } + + const updatedPurpose: Purpose = { + ...purpose.data, + versions: purpose.data.versions.filter( + (v) => v.id !== purposeVersion.id + ), + }; + + const event = toCreateEventWaitingForApprovalPurposeVersionDeleted({ + purpose: updatedPurpose, + version: purpose.metadata.version, + versionId, + correlationId, + }); + await repository.createEvent(event); + }, }; } From 51f0eb8d7aef6aba45ccd892aaf5bacb3a59f62e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 11 Apr 2024 17:49:33 +0200 Subject: [PATCH 102/537] Update router --- .../src/routers/PurposeRouter.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 6c9a7020f3..e6311bde49 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -19,6 +19,7 @@ import { config } from "../utilities/config.js"; import { purposeServiceBuilder } from "../services/purposeService.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { + deletePurposeVersionErrorMapper, getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, } from "../utilities/errorMappers.js"; @@ -120,7 +121,23 @@ const purposeRouter = ( .delete( "/purposes/:purposeId/versions/:versionId", authorizationMiddleware([ADMIN_ROLE, INTERNAL_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + await purposeService.deletePurposeVersion({ + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + authData: req.ctx.authData, + correlationId: req.ctx.correlationId, + }); + return res.status(204).end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + deletePurposeVersionErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .get( "/purposes/:purposeId/versions/:versionId/documents/:documentId", From 2238605f3f546b2df5a0cdb5dff619e78427398b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 09:00:37 +0200 Subject: [PATCH 103/537] Delete readme --- packages/purpose-readmodel-writer/README.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 packages/purpose-readmodel-writer/README.md diff --git a/packages/purpose-readmodel-writer/README.md b/packages/purpose-readmodel-writer/README.md deleted file mode 100644 index f2c7b5dc44..0000000000 --- a/packages/purpose-readmodel-writer/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# pagopa-interop-be-event-consumer-poc - -Kafka consumer that consumes Debezium events and updates the read model - -Node version required >=node16 - -Run consumer - -``` -pnpm start -``` From e7e1de1c5cdf22684774ac47c3f9d50206cf72ac Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:17:58 +0200 Subject: [PATCH 104/537] Update api spec --- packages/purpose-process/open-api/purpose-service-spec.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index 5744241fc7..ea9278bfb7 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -476,19 +476,19 @@ paths: '400': description: Bad Request content: - application/problem+json: + application/json: schema: $ref: '#/components/schemas/Problem' '403': description: Forbidden content: - application/problem+json: + application/json: schema: $ref: '#/components/schemas/Problem' '404': description: Purpose not found content: - application/problem+json: + application/json: schema: $ref: '#/components/schemas/Problem' description: reject the purpose version by id From 5a8b319d3507280619dcdb11ad83c3e2f059f5da Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:18:05 +0200 Subject: [PATCH 105/537] Add error --- packages/purpose-process/src/model/domain/errors.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a9e5f5d66f..160a036b6f 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -19,6 +19,7 @@ export const errorCodes = { organizationNotAllowed: "0007", organizationIsNotTheConsumer: "0008", purposeVersionCannotBeDeleted: "0009", + organizationIsNotTheProducer: "0010", }; export type ErrorCodes = keyof typeof errorCodes; @@ -110,3 +111,13 @@ export function purposeVersionCannotBeDeleted( title: "Organization not allowed", }); } + +export function organizationIsNotTheProducer( + organizationId: TenantId +): ApiError { + return new ApiError({ + detail: `Organization ${organizationId} is not allowed to perform the operation`, + code: "organizationIsNotTheProducer", + title: "Organization not allowed", + }); +} From 7c177c455f1c559c121cbd18264b7364139c3a9c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:18:16 +0200 Subject: [PATCH 106/537] Add event adapter --- .../src/model/domain/toEvent.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index 11632d9fce..fcddc7dc86 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -26,3 +26,24 @@ export const toCreateEventWaitingForApprovalPurposeVersionDeleted = ({ }, correlationId, }); + +export const toCreateEvenPurpsoeVersionRejected = ({ + purpose, + version, + versionId, + correlationId, +}: { + purpose: Purpose; + version: number; + versionId: PurposeVersionId; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "PurposeVersionRejected", + event_version: 2, + data: { purpose: toPurposeV2(purpose), versionId }, + }, + correlationId, +}); From b5a83759afbf675ba9f009ae58a5ecb6db578d42 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:18:26 +0200 Subject: [PATCH 107/537] Implement endpoint --- .../src/routers/PurposeRouter.ts | 19 +++++- .../src/services/purposeService.ts | 63 ++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index e6311bde49..60ebb61e5e 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -166,7 +166,24 @@ const purposeRouter = ( .post( "/purposes/:purposeId/versions/:versionId/reject", authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + await purposeService.rejectPurposeVersion({ + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + authData: req.ctx.authData, + rejectionReason: req.body.rejectionReason, + correlationId: req.ctx.correlationId, + }); + return res.status(204).end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + rejectPurposeVersionErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/purposes/:purposeId/versions/:versionId/activate", diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 8f55067170..24b7238f5e 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -27,6 +27,7 @@ import { import { eserviceNotFound, organizationIsNotTheConsumer, + organizationIsNotTheProducer, organizationNotAllowed, purposeNotFound, purposeVersionCannotBeDeleted, @@ -35,7 +36,10 @@ import { tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; -import { toCreateEventWaitingForApprovalPurposeVersionDeleted } from "../model/domain/toEvent.js"; +import { + toCreateEvenPurpsoeVersionRejected, + toCreateEventWaitingForApprovalPurposeVersionDeleted, +} from "../model/domain/toEvent.js"; import { ReadModelService } from "./readModelService.js"; const retrievePurpose = async ( @@ -204,6 +208,49 @@ export function purposeServiceBuilder( }); await repository.createEvent(event); }, + async rejectPurposeVersion({ + purposeId, + versionId, + rejectionReason, + authData, + correlationId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + rejectionReason: string; + authData: AuthData; + correlationId: string; + }): Promise { + const purpose = await retrievePurpose(purposeId, readModelService); + const eservice = await retrieveEService( + purpose.data.eserviceId, + readModelService + ); + if (authData.organizationId !== eservice.producerId) { + throw organizationIsNotTheProducer(authData.organizationId); + } + + const purposeVersion = retrievePurposeVersion(versionId, purpose); + + const updatedPurposeVersion: PurposeVersion = { + ...purposeVersion, + state: purposeVersionState.rejected, + rejectionReason, + }; + + const updatedPurpose = replacePurposeVersion( + purpose.data, + updatedPurposeVersion + ); + + const event = toCreateEvenPurpsoeVersionRejected({ + purpose: updatedPurpose, + version: purpose.metadata.version, + versionId, + correlationId, + }); + await repository.createEvent(event); + }, }; } @@ -279,3 +326,17 @@ const getOrganizationRole = ({ throw organizationNotAllowed(organizationId); } }; + +const replacePurposeVersion = ( + purpose: Purpose, + newVersion: PurposeVersion +): Purpose => { + const updatedVersions = purpose.versions.map((v: PurposeVersion) => + v.id === newVersion.id ? newVersion : v + ); + + return { + ...purpose, + versions: updatedVersions, + }; +}; From b32a8212f25eaec56c68b73f75fd3d37a658b995 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:23:12 +0200 Subject: [PATCH 108/537] Add error mapper --- packages/purpose-process/src/routers/PurposeRouter.ts | 1 + packages/purpose-process/src/utilities/errorMappers.ts | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 60ebb61e5e..4306c75e1a 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -22,6 +22,7 @@ import { deletePurposeVersionErrorMapper, getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, + rejectPurposeVersionErrorMapper, } from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index b344b78fe3..cd4905dbb5 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -37,3 +37,12 @@ export const deletePurposeVersionErrorMapper = ( .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("purposeVersionCannotBeDeleted", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const rejectPurposeVersionErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationIsNotTheProducer", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From f0adcdcd0fd77aef6303ab6cb4ea1898f307adaa Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:24:58 +0200 Subject: [PATCH 109/537] Add test placeholder --- .../purpose-process/test/purposeService.integration.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 24879bec3c..f08eb20bec 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -169,5 +169,11 @@ describe("database test", async () => { ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); }); + + describe("getRiskAnalysisDocumetn", () => { + it("test", () => { + expect(1).toBe(1); + }); + }); }); }); From 736a0e5d9407d75c2d8afc1ec6ba30e4f21705f3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:25:51 +0200 Subject: [PATCH 110/537] Add test placeholder --- .../purpose-process/test/purposeService.integration.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index f08eb20bec..425646a391 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -175,5 +175,11 @@ describe("database test", async () => { expect(1).toBe(1); }); }); + + describe("deletePurposeVersion", () => { + it("test", () => { + expect(2).toBe(2); + }); + }); }); }); From c7463ed93071c8efb04719c7c70e712fb96218bc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:26:03 +0200 Subject: [PATCH 111/537] Fix typo --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index f08eb20bec..9593d409c0 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -170,7 +170,7 @@ describe("database test", async () => { }); }); - describe("getRiskAnalysisDocumetn", () => { + describe("getRiskAnalysisDocument", () => { it("test", () => { expect(1).toBe(1); }); From 61a2587891bea7815af61ee1a010250064c3c079 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 10:26:59 +0200 Subject: [PATCH 112/537] Add test placeholder --- .../purpose-process/test/purposeService.integration.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 32f72185fc..f340457c39 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -181,5 +181,11 @@ describe("database test", async () => { expect(2).toBe(2); }); }); + + describe("rejectPurposeVersion", () => { + it("test", () => { + expect(3).toBe(3); + }); + }); }); }); From d712de3b4191055b4c4e45153e1a1fe636fb942e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 16:07:34 +0200 Subject: [PATCH 113/537] Add tests --- .../test/purposeService.integration.test.ts | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 24879bec3c..d7d3e2c242 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -26,8 +26,10 @@ import { import { StartedTestContainer } from "testcontainers"; import { EService, + EServiceId, Purpose, PurposeId, + TenantId, TenantKind, generateId, tenantKind, @@ -137,11 +139,60 @@ describe("database test", async () => { purposeService.getPurposeById(notExistingId, getMockAuthData()) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); - it("Should throw eserviceNotFound if the eservice doesn't exist", () => { - expect(1).toBe(1); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const notExistingId: EServiceId = generateId(); + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: notExistingId, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + getMockAuthData(mockTenant.id) + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: false, + }); }); - it("Should throw tenantNotFound if the tenant doesn't exist", () => { - expect(1).toBe(1); + it("Should throw tenantNotFound if the tenant doesn't exist", async () => { + const mockEService = getMockEService(); + const notExistingId: TenantId = generateId(); + + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + getMockAuthData(notExistingId) + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: false, + }); }); it("Should throw tenantKindNotFound if the tenant doesn't exist", async () => { const mockEService = getMockEService(); From de348cb3d27849c5edc3530c514a0f76ee8ca4e6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 16:11:21 +0200 Subject: [PATCH 114/537] Fix tests --- .../test/purposeService.integration.test.ts | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index d7d3e2c242..bb89a787c9 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -45,8 +45,10 @@ import { readModelServiceBuilder, } from "../src/services/readModelService.js"; import { + eserviceNotFound, purposeNotFound, tenantKindNotFound, + tenantNotFound, } from "../src/model/domain/errors.js"; import { addOnePurpose, getMockEService } from "./utils.js"; @@ -159,14 +161,12 @@ describe("database test", async () => { await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); - const result = await purposeService.getPurposeById( - mockPurpose1.id, - getMockAuthData(mockTenant.id) - ); - expect(result).toMatchObject({ - purpose: mockPurpose1, - isRiskAnalysisValid: false, - }); + expect( + purposeService.getPurposeById( + mockPurpose1.id, + getMockAuthData(mockTenant.id) + ) + ).rejects.toThrowError(eserviceNotFound(notExistingId)); }); it("Should throw tenantNotFound if the tenant doesn't exist", async () => { const mockEService = getMockEService(); @@ -185,14 +185,12 @@ describe("database test", async () => { await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); - const result = await purposeService.getPurposeById( - mockPurpose1.id, - getMockAuthData(notExistingId) - ); - expect(result).toMatchObject({ - purpose: mockPurpose1, - isRiskAnalysisValid: false, - }); + expect( + purposeService.getPurposeById( + mockPurpose1.id, + getMockAuthData(notExistingId) + ) + ).rejects.toThrowError(tenantNotFound(notExistingId)); }); it("Should throw tenantKindNotFound if the tenant doesn't exist", async () => { const mockEService = getMockEService(); From 8bf52a68cfd72302c8f5e50c6c981e417aafc6e7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 16:13:15 +0200 Subject: [PATCH 115/537] Add check --- packages/purpose-process/src/model/domain/apiConverter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index ac3b6f1a97..865f37c210 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -33,6 +33,9 @@ export const multiAnswersToApiMultiAnswers = ( multiAnswers: RiskAnalysisMultiAnswer[] ): Record => multiAnswers.reduce>((acc, curr) => { + if (curr.values.length === 0) { + return acc; + } // eslint-disable-next-line functional/immutable-data acc[curr.key] = curr.values; return acc; From 1581cb7046be7a079d592a2b068709e4edb0737a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 16:17:42 +0200 Subject: [PATCH 116/537] Update mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 1be556ade6..e28e27c418 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -12,4 +12,5 @@ export const getPurposeErrorMapper = (error: ApiError): number => .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 27967c2cd0b0552a8712fc8d29cff683cb2171be Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 17:24:27 +0200 Subject: [PATCH 117/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 24b7238f5e..004ac6af49 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -221,6 +221,8 @@ export function purposeServiceBuilder( authData: AuthData; correlationId: string; }): Promise { + logger.info(`Rejecting Version ${versionId} in Purpose ${purposeId}`); + const purpose = await retrievePurpose(purposeId, readModelService); const eservice = await retrieveEService( purpose.data.eserviceId, From d274946f286752ac6baa7bc560a059164387f973 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 12 Apr 2024 17:24:47 +0200 Subject: [PATCH 118/537] Set updatedAt --- packages/purpose-process/src/services/purposeService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 004ac6af49..5feedbc69a 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -238,6 +238,7 @@ export function purposeServiceBuilder( ...purposeVersion, state: purposeVersionState.rejected, rejectionReason, + updatedAt: new Date(), }; const updatedPurpose = replacePurposeVersion( From 0397669a9d08c397691d70db3c26763081223e09 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 10:20:43 +0200 Subject: [PATCH 119/537] Add error and check --- packages/purpose-process/src/model/domain/errors.ts | 13 +++++++++++++ .../purpose-process/src/services/purposeService.ts | 4 ++++ .../purpose-process/src/utilities/errorMappers.ts | 2 ++ 3 files changed, 19 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 160a036b6f..b333a77ffe 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -4,6 +4,7 @@ import { PurposeId, PurposeVersionDocumentId, PurposeVersionId, + PurposeVersionState, TenantId, makeApiProblemBuilder, } from "pagopa-interop-models"; @@ -20,6 +21,7 @@ export const errorCodes = { organizationIsNotTheConsumer: "0008", purposeVersionCannotBeDeleted: "0009", organizationIsNotTheProducer: "0010", + notValidVersionState: "0011", }; export type ErrorCodes = keyof typeof errorCodes; @@ -121,3 +123,14 @@ export function organizationIsNotTheProducer( title: "Organization not allowed", }); } + +export function notValidVersionState( + purposeVersionId: PurposeVersionId, + versionState: PurposeVersionState +): ApiError { + return new ApiError({ + detail: `Purpose version ${purposeVersionId} has a not valid state for this operation: ${versionState}`, + code: "notValidVersionState", + title: "Not valid purpose version state", + }); +} diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 5feedbc69a..a9597ba316 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -26,6 +26,7 @@ import { } from "pagopa-interop-models"; import { eserviceNotFound, + notValidVersionState, organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, @@ -234,6 +235,9 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); + if (purposeVersion.state !== purposeVersionState.waitingForApproval) { + throw notValidVersionState(purposeVersion.id, purposeVersion.state); + } const updatedPurposeVersion: PurposeVersion = { ...purposeVersion, state: purposeVersionState.rejected, diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index cd4905dbb5..aea8943885 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -10,6 +10,7 @@ const { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_FORBIDDEN, HTTP_STATUS_CONFLICT, + HTTP_STATUS_BAD_REQUEST, } = constants; export const getPurposeErrorMapper = (error: ApiError): number => @@ -45,4 +46,5 @@ export const rejectPurposeVersionErrorMapper = ( .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) .with("organizationIsNotTheProducer", () => HTTP_STATUS_FORBIDDEN) + .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 3b490d2b2edc7c8c774e7b53fbbe6434740c80a1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:01:25 +0200 Subject: [PATCH 120/537] Fix logic --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c814605200..6be1951f01 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -66,7 +66,7 @@ const retrievePurposeVersionDocument = ( ): PurposeVersionDocument => { const document = purposeVersion.riskAnalysis; - if (document === undefined) { + if (document === undefined || document.id !== documentId) { throw purposeVersionDocumentNotFound( purposeId, purposeVersion.id, From 26317943327faaa891a33af53a9ee2d3ae671a72 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:01:35 +0200 Subject: [PATCH 121/537] Add mock function --- packages/commons-test/src/testUtils.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 7975b5f560..9727a15e8b 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -22,6 +22,7 @@ import { descriptorState, generateId, tenantAttributeType, + PurposeVersionDocument, } from "pagopa-interop-models"; import { v4 as uuidv4 } from "uuid"; import { AuthData } from "pagopa-interop-commons"; @@ -194,3 +195,10 @@ export const getMockTenant = (): Tenant => ({ features: [], mails: [], }); + +export const getMockPurposeVersionDocument = (): PurposeVersionDocument => ({ + path: "path", + id: generateId(), + contentType: "json", + createdAt: new Date(), +}); From 83612467b8b4ff0248f41bc940c123ef9eae6c87 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:01:47 +0200 Subject: [PATCH 122/537] Update error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index ac33195b81..d2a1408f7b 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -26,4 +26,6 @@ export const getRiskAnalysisDocumentErrorMapper = ( .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) + .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("purposeVersionDocumentNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From bfe7ab2e39e9920a6e7c27c01f779e46ed1dde8a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:01:51 +0200 Subject: [PATCH 123/537] Add tests --- .../test/purposeService.integration.test.ts | 183 +++++++++++++++++- 1 file changed, 181 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index d6d2c5349c..2c948f9af4 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -18,6 +18,8 @@ import { TEST_POSTGRES_DB_PORT, getMockAuthData, getMockPurpose, + getMockPurposeVersion, + getMockPurposeVersionDocument, getMockTenant, mongoDBContainer, postgreSQLContainer, @@ -29,6 +31,8 @@ import { EServiceId, Purpose, PurposeId, + PurposeVersionDocumentId, + PurposeVersionId, TenantId, TenantKind, generateId, @@ -46,7 +50,10 @@ import { } from "../src/services/readModelService.js"; import { eserviceNotFound, + organizationNotAllowed, purposeNotFound, + purposeVersionDocumentNotFound, + purposeVersionNotFound, tenantKindNotFound, tenantNotFound, } from "../src/model/domain/errors.js"; @@ -220,8 +227,180 @@ describe("database test", async () => { }); describe("getRiskAnalysisDocument", () => { - it("test", () => { - expect(1).toBe(1); + it("Should get the purpose version document", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockDocument = getMockPurposeVersionDocument(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const result = await purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + authData: getMockAuthData(mockEService.producerId), + }); + expect(result).toEqual(mockDocument); + }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockDocument = getMockPurposeVersionDocument(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + authData: getMockAuthData(mockEService.producerId), + }) + ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); + }); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockDocument = getMockPurposeVersionDocument(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + authData: getMockAuthData(mockEService.producerId), + }) + ).rejects.toThrowError(eserviceNotFound(mockEService.id)); + }); + it("Should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const randomVersionId: PurposeVersionId = generateId(); + const randomDocumentId: PurposeVersionDocumentId = generateId(); + const mockDocument = getMockPurposeVersionDocument(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: randomVersionId, + documentId: randomDocumentId, + authData: getMockAuthData(mockEService.producerId), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose1.id, randomVersionId) + ); + }); + it("Should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockDocument = getMockPurposeVersionDocument(); + const randomDocumentId: PurposeVersionDocumentId = generateId(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: randomDocumentId, + authData: getMockAuthData(mockEService.producerId), + }) + ).rejects.toThrowError( + purposeVersionDocumentNotFound( + mockPurpose1.id, + mockPurposeVersion.id, + randomDocumentId + ) + ); + }); + it("Should throw organizationNotAllowed if the requester is not the producer not the consumer", async () => { + const randomId: TenantId = generateId(); + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockDocument = getMockPurposeVersionDocument(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + authData: getMockAuthData(randomId), + }) + ).rejects.toThrowError(organizationNotAllowed(randomId)); }); }); }); From 48597fb0ba6717955456f50dae3db876d710f6ae Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:30:06 +0200 Subject: [PATCH 124/537] Add tests --- .../src/model/domain/errors.ts | 2 +- .../test/purposeService.integration.test.ts | 219 +++++++++++++++++- 2 files changed, 219 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a9e5f5d66f..c6f5dae4e2 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -107,6 +107,6 @@ export function purposeVersionCannotBeDeleted( return new ApiError({ detail: `Version ${versionId} of Purpose ${purposeId} cannot be deleted`, code: "purposeVersionCannotBeDeleted", - title: "Organization not allowed", + title: "Purpose version canont be deleted", }); } diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index da96c0a582..b49c478541 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -36,6 +36,7 @@ import { TenantId, TenantKind, generateId, + purposeVersionState, tenantKind, toReadModelEService, } from "pagopa-interop-models"; @@ -50,8 +51,10 @@ import { } from "../src/services/readModelService.js"; import { eserviceNotFound, + organizationIsNotTheConsumer, organizationNotAllowed, purposeNotFound, + purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, tenantKindNotFound, @@ -405,9 +408,223 @@ describe("database test", async () => { }); describe("deletePurposeVersion", () => { - it("test", () => { + it("Should write in event-store for the deletion of a purpose (purpose with no versions)", () => { expect(2).toBe(2); }); + it("Should write in event-store for the deletion of a purpose (purpose with only a draft version)", () => { + expect(2).toBe(2); + }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); + }); + it("Should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: randomVersionId, + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose1.id, randomVersionId) + ); + }); + it("Should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const randomVersionId: PurposeVersionId = generateId(); + const randomId: TenantId = generateId(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: randomVersionId, + authData: getMockAuthData(randomId), + correlationId: generateId(), + }) + ).rejects.toThrowError(organizationIsNotTheConsumer(randomId)); + }); + it("Should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [ + { ...mockPurposeVersion, state: purposeVersionState.draft }, + ], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + ); + }); + it("Should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.active, + }, + ], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + ); + }); + it("Should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [ + { ...mockPurposeVersion, state: purposeVersionState.archived }, + ], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + ); + }); + it("Should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [ + { ...mockPurposeVersion, state: purposeVersionState.suspended }, + ], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + ); + }); }); }); }); From 09eba74ea7f0e0fe308c694ef5185e53780edeb2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:43:41 +0200 Subject: [PATCH 125/537] Update test util --- packages/commons-test/src/eventStoreTestUtils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/commons-test/src/eventStoreTestUtils.ts b/packages/commons-test/src/eventStoreTestUtils.ts index 8e0d446cbe..c59b1af525 100644 --- a/packages/commons-test/src/eventStoreTestUtils.ts +++ b/packages/commons-test/src/eventStoreTestUtils.ts @@ -5,6 +5,7 @@ import { AgreementId, AttributeId, EServiceId, + PurposeId, TenantId, protobufDecoder, } from "pagopa-interop-models"; @@ -57,6 +58,8 @@ export async function readLastEventByStreamId( ? EServiceId : T extends "tenant" ? TenantId + : T extends "purpose" + ? PurposeId : never, schema: T, postgresDB: IDatabase From 5f81fc54091ff036ead5a2534fe08c2d8943fbdb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 11:45:46 +0200 Subject: [PATCH 126/537] Add test --- .../test/purposeService.integration.test.ts | 90 +++++++++++-------- 1 file changed, 53 insertions(+), 37 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index b49c478541..0903075d5d 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -16,6 +16,7 @@ import { IDatabase } from "pg-promise"; import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, + decodeProtobufPayload, getMockAuthData, getMockPurpose, getMockPurposeVersion, @@ -23,6 +24,7 @@ import { getMockTenant, mongoDBContainer, postgreSQLContainer, + readLastEventByStreamId, writeInReadmodel, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; @@ -35,9 +37,11 @@ import { PurposeVersionId, TenantId, TenantKind, + WaitingForApprovalPurposeVersionDeletedV2, generateId, purposeVersionState, tenantKind, + toPurposeV2, toReadModelEService, } from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; @@ -408,11 +412,54 @@ describe("database test", async () => { }); describe("deletePurposeVersion", () => { - it("Should write in event-store for the deletion of a purpose (purpose with no versions)", () => { - expect(2).toBe(2); - }); - it("Should write in event-store for the deletion of a purpose (purpose with only a draft version)", () => { - expect(2).toBe(2); + it("Should write in event-store for the deletion of a purpose version", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.waitingForApproval, + }, + ], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "WaitingForApprovalPurposeVersionDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: WaitingForApprovalPurposeVersionDeletedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose1, + versions: [], + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); it("Should throw purposeNotFound if the purpose doesn't exist", async () => { const mockEService = getMockEService(); @@ -472,27 +519,20 @@ describe("database test", async () => { it("Should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); - const randomVersionId: PurposeVersionId = generateId(); const randomId: TenantId = generateId(); const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, - versionId: randomVersionId, + versionId: mockPurposeVersion.id, authData: getMockAuthData(randomId), correlationId: generateId(), }) @@ -508,14 +548,8 @@ describe("database test", async () => { { ...mockPurposeVersion, state: purposeVersionState.draft }, ], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -542,14 +576,8 @@ describe("database test", async () => { }, ], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -573,14 +601,8 @@ describe("database test", async () => { { ...mockPurposeVersion, state: purposeVersionState.archived }, ], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -604,14 +626,8 @@ describe("database test", async () => { { ...mockPurposeVersion, state: purposeVersionState.suspended }, ], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( From 18cef40bf47b67c945b986d802f769bddad85f61 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 15 Apr 2024 11:52:53 +0200 Subject: [PATCH 127/537] implemented update e reverseUpdate --- packages/purpose-process/package.json | 2 + .../src/model/domain/errors.ts | 46 +++- .../src/model/domain/models.ts | 12 + .../src/model/domain/toEvent.ts | 23 +- .../src/routers/PurposeRouter.ts | 44 +++- .../src/services/purposeService.ts | 224 +++++++++++++++--- .../src/services/validators.ts | 98 ++++++++ .../src/utilities/errorMappers.ts | 14 ++ pnpm-lock.yaml | 6 + 9 files changed, 430 insertions(+), 39 deletions(-) create mode 100644 packages/purpose-process/src/services/validators.ts diff --git a/packages/purpose-process/package.json b/packages/purpose-process/package.json index 3ee848d0d2..f8a2f130f6 100644 --- a/packages/purpose-process/package.json +++ b/packages/purpose-process/package.json @@ -23,6 +23,7 @@ "@types/dotenv-flow": "3.2.0", "@types/express": "4.17.17", "@types/node": "20.3.1", + "@types/uuid": "9.0.2", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.2.2", @@ -41,6 +42,7 @@ "pagopa-interop-models": "workspace:*", "pg-promise": "11.5.0", "ts-pattern": "5.0.6", + "uuid": "9.0.0", "zod": "3.21.4" } } diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 160a036b6f..cc74b26d15 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -1,13 +1,14 @@ import { ApiError, EServiceId, + EServiceMode, PurposeId, PurposeVersionDocumentId, PurposeVersionId, TenantId, makeApiProblemBuilder, } from "pagopa-interop-models"; -import { logger } from "pagopa-interop-commons"; +import { RiskAnalysisValidationIssue, logger } from "pagopa-interop-commons"; export const errorCodes = { purposeNotFound: "0001", @@ -20,6 +21,10 @@ export const errorCodes = { organizationIsNotTheConsumer: "0008", purposeVersionCannotBeDeleted: "0009", organizationIsNotTheProducer: "0010", + eServiceModeNotAllowed: "0011", + missingFreeOfChargeReason: "0012", + riskAnalysisValidationFailed: "0013", + purposeNotInDraftState: "0014", }; export type ErrorCodes = keyof typeof errorCodes; @@ -121,3 +126,42 @@ export function organizationIsNotTheProducer( title: "Organization not allowed", }); } + +export function eServiceModeNotAllowed( + eserviceId: EServiceId, + mode: EServiceMode +): ApiError { + return new ApiError({ + detail: `EService ${eserviceId} has not ${mode} mode`, + code: "eServiceModeNotAllowed", + title: "EService mode not allowed", + }); +} + +export function missingFreeOfChargeReason(): ApiError { + return new ApiError({ + detail: "Missing free of charge reason", + code: "missingFreeOfChargeReason", + title: "Missing free of charge reason", + }); +} + +export function riskAnalysisValidationFailed( + reasons: RiskAnalysisValidationIssue[] +): ApiError { + return new ApiError({ + detail: `Risk analysis validation failed. Reasons: ${reasons}`, + code: "riskAnalysisValidationFailed", + title: "Risk analysis validation failed", + }); +} + +export function purposeNotInDraftState( + purposeId: PurposeId +): ApiError { + return new ApiError({ + detail: `Purpose ${purposeId} is not in draft state`, + code: "purposeNotInDraftState", + title: "Purpose not in draft state", + }); +} \ No newline at end of file diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 9a97747326..83c06470f9 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -10,3 +10,15 @@ export type ApiPurposeVersionState = z.infer< export type ApiPurposeVersionDocument = z.infer< typeof api.schemas.PurposeVersionDocument >; + +export type PurposeUpdateContent = z.infer< + typeof api.schemas.PurposeUpdateContent +>; + +export type RiskAnalysisFormSeed = z.infer< + typeof api.schemas.RiskAnalysisFormSeed +>; + +export type ReversePurposeUpdateContent = z.infer< + typeof api.schemas.ReversePurposeUpdateContent +>; diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index fcddc7dc86..5283d1b23c 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -27,7 +27,7 @@ export const toCreateEventWaitingForApprovalPurposeVersionDeleted = ({ correlationId, }); -export const toCreateEvenPurpsoeVersionRejected = ({ +export const toCreateEvenPurposeVersionRejected = ({ purpose, version, versionId, @@ -47,3 +47,24 @@ export const toCreateEvenPurpsoeVersionRejected = ({ }, correlationId, }); + +export const toCreateEventDraftPurposeUpdated = ({ + purpose, + version, + correlationId, +}: { + purpose: Purpose; + version: number; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "DraftPurposeUpdated", + event_version: 2, + data: { + purpose: toPurposeV2(purpose), + }, + }, + correlationId, +}); diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 4306c75e1a..48371c24ff 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -23,6 +23,7 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, + updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( @@ -78,8 +79,24 @@ const purposeRouter = ( .post( "/reverse/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() - ) + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.updateReversePurpose({ + purposeId: unsafeBrandId(req.params.id), + reversePurposeUpdateContent: req.body, + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, updatePurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .get( "/purposes/:id", authorizationMiddleware([ @@ -106,8 +123,27 @@ const purposeRouter = ( } } ) - .post("/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => - res.status(501).send() + .post( + "/purposes/:id", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.updatePurpose({ + purposeId: unsafeBrandId(req.params.id), + purposeUpdateContent: req.body, + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, updatePurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .delete( "/purposes/:id", diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 24b7238f5e..b135d04ed5 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,10 +1,11 @@ import { AuthData, + CreateEvent, DB, eventRepository, logger, + riskAnalysisValidatedFormToNewRiskAnalysisForm, riskAnalysisFormToRiskAnalysisFormToValidate, - validateRiskAnalysis, } from "pagopa-interop-commons"; import { EService, @@ -16,31 +17,47 @@ import { PurposeId, TenantKind, purposeVersionState, - RiskAnalysisForm, PurposeVersionId, PurposeVersionDocumentId, PurposeVersion, PurposeVersionDocument, ownership, purposeEventToBinaryData, + PurposeRiskAnalysisForm, + PurposeEvent, + EServiceMode, } from "pagopa-interop-models"; import { eserviceNotFound, - organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, purposeNotFound, purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, - tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; import { - toCreateEvenPurpsoeVersionRejected, + toCreateEvenPurposeVersionRejected, + toCreateEventDraftPurposeUpdated, toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; +import { + PurposeUpdateContent, + ReversePurposeUpdateContent, + RiskAnalysisFormSeed, +} from "../model/domain/models.js"; import { ReadModelService } from "./readModelService.js"; +import { + assertOrganizationIsAConsumer, + isEserviceMode, + isFreeOfCharge, + isRiskAnalysisFormValid, + purposeIsDraft, + validateRiskAnalysisSchemaOrThrow, + assertTenantKindExists, + assertIsDraft, +} from "./validators.js"; const retrievePurpose = async ( purposeId: PurposeId, @@ -132,9 +149,7 @@ export function purposeServiceBuilder( readModelService ); - if (tenant.kind === undefined) { - throw tenantKindNotFound(tenant.id); - } + assertTenantKindExists(tenant); return authorizeRiskAnalysisForm({ purpose: purpose.data, @@ -183,9 +198,10 @@ export function purposeServiceBuilder( const purpose = await retrievePurpose(purposeId, readModelService); - if (authData.organizationId !== purpose.data.consumerId) { - throw organizationIsNotTheConsumer(authData.organizationId); - } + assertOrganizationIsAConsumer( + authData.organizationId, + purpose.data.consumerId + ); const purposeVersion = retrievePurposeVersion(versionId, purpose); @@ -243,7 +259,7 @@ export function purposeServiceBuilder( updatedPurposeVersion ); - const event = toCreateEvenPurpsoeVersionRejected({ + const event = toCreateEvenPurposeVersionRejected({ purpose: updatedPurpose, version: purpose.metadata.version, versionId, @@ -251,6 +267,44 @@ export function purposeServiceBuilder( }); await repository.createEvent(event); }, + async updatePurpose({ + purposeId, + purposeUpdateContent, + organizationId, + correlationId, + }: { + purposeId: PurposeId; + purposeUpdateContent: PurposeUpdateContent; + organizationId: TenantId; + correlationId: string; + }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + return await updatePurposeInternal( + purposeId, + purposeUpdateContent, + organizationId, + "Deliver", + { readModelService, correlationId, repository } + ); + }, + async updateReversePurpose({ + purposeId, + reversePurposeUpdateContent, + organizationId, + correlationId, + }: { + purposeId: PurposeId; + reversePurposeUpdateContent: ReversePurposeUpdateContent; + organizationId: TenantId; + correlationId: string; + }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + return await updatePurposeInternal( + purposeId, + reversePurposeUpdateContent, + organizationId, + "Receive", + { readModelService, correlationId, repository } + ); + }, }; } @@ -286,27 +340,6 @@ const authorizeRiskAnalysisForm = ({ } }; -const isRiskAnalysisFormValid = ( - riskAnalysisForm: RiskAnalysisForm | undefined, - schemaOnlyValidation: boolean, - tenantKind: TenantKind -): boolean => { - if (riskAnalysisForm === undefined) { - return false; - } else { - return ( - validateRiskAnalysis( - riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), - schemaOnlyValidation, - tenantKind - ).type === "valid" - ); - } -}; - -const purposeIsDraft = (purpose: Purpose): boolean => - !purpose.versions.some((v) => v.state !== purposeVersionState.draft); - const getOrganizationRole = ({ organizationId, producerId, @@ -340,3 +373,128 @@ const replacePurposeVersion = ( versions: updatedVersions, }; }; + +const getInvolvedTenantByEServiceMode = async ( + eservice: EService, + consumerId: TenantId, + readModelService: ReadModelService +): Promise => { + if (eservice.mode === "Deliver") { + return retrieveTenant(consumerId, readModelService); + } else { + return retrieveTenant(eservice.producerId, readModelService); + } +}; + +const validateAndTransformRiskAnalysis = ( + riskAnalysisForm: RiskAnalysisFormSeed | undefined, + tenantKind: TenantKind +): PurposeRiskAnalysisForm | undefined => { + if (!riskAnalysisForm) { + return undefined; + } + + const validatedForm = validateRiskAnalysisSchemaOrThrow( + riskAnalysisForm, + tenantKind + ); + + return { + ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), + riskAnalysisId: undefined, + }; +}; + +const reverseValidateAndTransformRiskAnalysis = ( + riskAnalysisForm: PurposeRiskAnalysisForm | undefined, + tenantKind: TenantKind +): PurposeRiskAnalysisForm | undefined => { + if (!riskAnalysisForm) { + return undefined; + } + + const formToValidate = + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm); + const validatedForm = validateRiskAnalysisSchemaOrThrow( + formToValidate, + tenantKind + ); + + return { + ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), + riskAnalysisId: riskAnalysisForm.riskAnalysisId, + }; +}; + +const updatePurposeInternal = async ( + purposeId: PurposeId, + updateContent: PurposeUpdateContent | ReversePurposeUpdateContent, + organizationId: TenantId, + eserviceMode: EServiceMode, + { + readModelService, + correlationId, + repository, + }: { + readModelService: ReadModelService; + correlationId: string; + repository: { + createEvent: (createEvent: CreateEvent) => Promise; + }; + } +): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { + const purpose = await retrievePurpose(purposeId, readModelService); + assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); + assertIsDraft(purpose.data); + + const eservice = await retrieveEService( + purpose.data.eserviceId, + readModelService + ); + isEserviceMode(eservice.id, eserviceMode); + isFreeOfCharge( + updateContent.isFreeOfCharge, + updateContent.freeOfChargeReason + ); + + const tenant = await getInvolvedTenantByEServiceMode( + eservice, + purpose.data.consumerId, + readModelService + ); + + assertTenantKindExists(tenant); + + const newRiskAnalysis: PurposeRiskAnalysisForm | undefined = + eserviceMode === "Deliver" + ? validateAndTransformRiskAnalysis( + (updateContent as PurposeUpdateContent).riskAnalysisForm, + tenant.kind + ) + : reverseValidateAndTransformRiskAnalysis( + purpose.data.riskAnalysisForm, + tenant.kind + ); + + const updatedPurpose: Purpose = { + ...purpose.data, + ...updateContent, + riskAnalysisForm: newRiskAnalysis, + }; + + const event = toCreateEventDraftPurposeUpdated({ + purpose: updatedPurpose, + version: purpose.metadata.version, + correlationId, + }); + await repository.createEvent(event); + + return { + purpose: updatedPurpose, + isRiskAnalysisValid: isRiskAnalysisFormValid( + updatedPurpose.riskAnalysisForm, + false, + tenant.kind + ), + }; +}; diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts new file mode 100644 index 0000000000..2427fc2332 --- /dev/null +++ b/packages/purpose-process/src/services/validators.ts @@ -0,0 +1,98 @@ +import { + EServiceId, + EServiceMode, + Purpose, + RiskAnalysisForm, + Tenant, + TenantId, + TenantKind, + purposeVersionState, +} from "pagopa-interop-models"; +import { + validateRiskAnalysis, + riskAnalysisFormToRiskAnalysisFormToValidate, + RiskAnalysisValidatedForm, +} from "pagopa-interop-commons"; +import { + eServiceModeNotAllowed, + missingFreeOfChargeReason, + organizationIsNotTheConsumer, + purposeNotInDraftState, + riskAnalysisValidationFailed, + tenantKindNotFound, +} from "../model/domain/errors.js"; +import { RiskAnalysisFormSeed } from "../model/domain/models.js"; + +export const isEserviceMode = ( + eserviceId: EServiceId, + mode: EServiceMode +): void => { + if (mode !== mode) { + throw eServiceModeNotAllowed(eserviceId, mode); + } +}; + +export const purposeIsDraft = (purpose: Purpose): boolean => + !purpose.versions.some((v) => v.state !== purposeVersionState.draft); + +export const isRiskAnalysisFormValid = ( + riskAnalysisForm: RiskAnalysisForm | undefined, + schemaOnlyValidation: boolean, + tenantKind: TenantKind +): boolean => { + if (riskAnalysisForm === undefined) { + return false; + } else { + return ( + validateRiskAnalysis( + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), + schemaOnlyValidation, + tenantKind + ).type === "valid" + ); + } +}; + +export const isFreeOfCharge = ( + isFreeOfCharge: boolean, + freeOfChargeReason: string | undefined +): void => { + if (isFreeOfCharge && !freeOfChargeReason) { + throw missingFreeOfChargeReason(); + } +}; + +export const assertOrganizationIsAConsumer = ( + organizationId: TenantId, + consumerId: TenantId +): void => { + if (organizationId !== consumerId) { + throw organizationIsNotTheConsumer(organizationId); + } +}; + +export function validateRiskAnalysisSchemaOrThrow( + riskAnalysisForm: RiskAnalysisFormSeed, + tenantKind: TenantKind +): RiskAnalysisValidatedForm { + const result = validateRiskAnalysis(riskAnalysisForm, true, tenantKind); + if (result.type === "invalid") { + throw riskAnalysisValidationFailed(result.issues); + } else { + return result.value; + } +} + +export function assertTenantKindExists( + tenant: Tenant +): asserts tenant is Tenant & { kind: NonNullable } { + if (!tenant.kind) { + throw tenantKindNotFound(tenant.id); + } +} + +export function assertIsDraft(purpose: Purpose): void { + if (!purposeIsDraft(purpose)) { + throw purposeNotInDraftState(purpose.id); + } +} diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index cd4905dbb5..5282a1206b 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -10,6 +10,7 @@ const { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_FORBIDDEN, HTTP_STATUS_CONFLICT, + HTTP_STATUS_BAD_REQUEST, } = constants; export const getPurposeErrorMapper = (error: ApiError): number => @@ -46,3 +47,16 @@ export const rejectPurposeVersionErrorMapper = ( .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) .with("organizationIsNotTheProducer", () => HTTP_STATUS_FORBIDDEN) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const updatePurposeErrorMapper = (error: ApiError): number => + match(error.code) + .with("eServiceModeNotAllowed", () => HTTP_STATUS_BAD_REQUEST) + .with("missingFreeOfChargeReason", () => HTTP_STATUS_BAD_REQUEST) + .with("tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with("purposeNotInDraftState", () => HTTP_STATUS_FORBIDDEN) + .with("eserviceNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f44e020757..9bb4a8f3a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -905,6 +905,9 @@ importers: ts-pattern: specifier: 5.0.6 version: 5.0.6 + uuid: + specifier: 9.0.0 + version: 9.0.0 zod: specifier: 3.21.4 version: 3.21.4 @@ -921,6 +924,9 @@ importers: '@types/node': specifier: 20.3.1 version: 20.3.1 + '@types/uuid': + specifier: 9.0.2 + version: 9.0.2 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test From 0a176767f28e6d40e697166fcd6b49290c91c07b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 12:02:56 +0200 Subject: [PATCH 128/537] Fix typo --- packages/purpose-process/src/model/domain/toEvent.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index fcddc7dc86..b768e74a47 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -27,7 +27,7 @@ export const toCreateEventWaitingForApprovalPurposeVersionDeleted = ({ correlationId, }); -export const toCreateEvenPurpsoeVersionRejected = ({ +export const toCreateEventPurposeVersionRejected = ({ purpose, version, versionId, diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 554d8d7421..ed8780a5d7 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -38,7 +38,7 @@ import { tenantNotFound, } from "../model/domain/errors.js"; import { - toCreateEvenPurpsoeVersionRejected, + toCreateEventPurposeVersionRejected, toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { ReadModelService } from "./readModelService.js"; @@ -250,7 +250,7 @@ export function purposeServiceBuilder( updatedPurposeVersion ); - const event = toCreateEvenPurpsoeVersionRejected({ + const event = toCreateEventPurposeVersionRejected({ purpose: updatedPurpose, version: purpose.metadata.version, versionId, From 6f8c71e19eaec0299dddc8290320b2d33e039e84 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 12:05:48 +0200 Subject: [PATCH 129/537] Refactor --- .../src/services/purposeService.ts | 32 ++----------------- .../src/services/validators.ts | 31 ++++++++++++++++++ 2 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 packages/purpose-process/src/services/validators.ts diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ed8780a5d7..3580571746 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,11 +1,4 @@ -import { - AuthData, - DB, - eventRepository, - logger, - riskAnalysisFormToRiskAnalysisFormToValidate, - validateRiskAnalysis, -} from "pagopa-interop-commons"; +import { AuthData, DB, eventRepository, logger } from "pagopa-interop-commons"; import { EService, EServiceId, @@ -16,7 +9,6 @@ import { PurposeId, TenantKind, purposeVersionState, - RiskAnalysisForm, PurposeVersionId, PurposeVersionDocumentId, PurposeVersion, @@ -42,6 +34,7 @@ import { toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { ReadModelService } from "./readModelService.js"; +import { isRiskAnalysisFormValid, purposeIsDraft } from "./validators.js"; const retrievePurpose = async ( purposeId: PurposeId, @@ -293,27 +286,6 @@ const authorizeRiskAnalysisForm = ({ } }; -const isRiskAnalysisFormValid = ( - riskAnalysisForm: RiskAnalysisForm | undefined, - schemaOnlyValidation: boolean, - tenantKind: TenantKind -): boolean => { - if (riskAnalysisForm === undefined) { - return false; - } else { - return ( - validateRiskAnalysis( - riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), - schemaOnlyValidation, - tenantKind - ).type === "valid" - ); - } -}; - -const purposeIsDraft = (purpose: Purpose): boolean => - !purpose.versions.some((v) => v.state !== purposeVersionState.draft); - const getOrganizationRole = ({ organizationId, producerId, diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts new file mode 100644 index 0000000000..4c9c595753 --- /dev/null +++ b/packages/purpose-process/src/services/validators.ts @@ -0,0 +1,31 @@ +import { + riskAnalysisFormToRiskAnalysisFormToValidate, + validateRiskAnalysis, +} from "pagopa-interop-commons"; +import { + Purpose, + RiskAnalysisForm, + TenantKind, + purposeVersionState, +} from "pagopa-interop-models"; + +export const isRiskAnalysisFormValid = ( + riskAnalysisForm: RiskAnalysisForm | undefined, + schemaOnlyValidation: boolean, + tenantKind: TenantKind +): boolean => { + if (riskAnalysisForm === undefined) { + return false; + } else { + return ( + validateRiskAnalysis( + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), + schemaOnlyValidation, + tenantKind + ).type === "valid" + ); + } +}; + +export const purposeIsDraft = (purpose: Purpose): boolean => + !purpose.versions.some((v) => v.state !== purposeVersionState.draft); From 19d67f5f2d987ceefb9b444cc167577cd30ca0e5 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 15 Apr 2024 12:05:53 +0200 Subject: [PATCH 130/537] moved riskAnalysis functions --- .../src/services/purposeService.ts | 46 +------------------ .../src/services/validators.ts | 42 +++++++++++++++++ 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index f0d9061ad4..2fd9ae691b 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -4,8 +4,6 @@ import { DB, eventRepository, logger, - riskAnalysisValidatedFormToNewRiskAnalysisForm, - riskAnalysisFormToRiskAnalysisFormToValidate, } from "pagopa-interop-commons"; import { EService, @@ -46,7 +44,6 @@ import { import { PurposeUpdateContent, ReversePurposeUpdateContent, - RiskAnalysisFormSeed, } from "../model/domain/models.js"; import { ReadModelService } from "./readModelService.js"; import { @@ -55,9 +52,10 @@ import { isFreeOfCharge, isRiskAnalysisFormValid, purposeIsDraft, - validateRiskAnalysisSchemaOrThrow, assertTenantKindExists, assertIsDraft, + reverseValidateAndTransformRiskAnalysis, + validateAndTransformRiskAnalysis, } from "./validators.js"; const retrievePurpose = async ( @@ -393,46 +391,6 @@ const getInvolvedTenantByEServiceMode = async ( } }; -const validateAndTransformRiskAnalysis = ( - riskAnalysisForm: RiskAnalysisFormSeed | undefined, - tenantKind: TenantKind -): PurposeRiskAnalysisForm | undefined => { - if (!riskAnalysisForm) { - return undefined; - } - - const validatedForm = validateRiskAnalysisSchemaOrThrow( - riskAnalysisForm, - tenantKind - ); - - return { - ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), - riskAnalysisId: undefined, - }; -}; - -const reverseValidateAndTransformRiskAnalysis = ( - riskAnalysisForm: PurposeRiskAnalysisForm | undefined, - tenantKind: TenantKind -): PurposeRiskAnalysisForm | undefined => { - if (!riskAnalysisForm) { - return undefined; - } - - const formToValidate = - riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm); - const validatedForm = validateRiskAnalysisSchemaOrThrow( - formToValidate, - tenantKind - ); - - return { - ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), - riskAnalysisId: riskAnalysisForm.riskAnalysisId, - }; -}; - const updatePurposeInternal = async ( purposeId: PurposeId, updateContent: PurposeUpdateContent | ReversePurposeUpdateContent, diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 2427fc2332..7a5d8c0c60 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -2,6 +2,7 @@ import { EServiceId, EServiceMode, Purpose, + PurposeRiskAnalysisForm, RiskAnalysisForm, Tenant, TenantId, @@ -12,6 +13,7 @@ import { validateRiskAnalysis, riskAnalysisFormToRiskAnalysisFormToValidate, RiskAnalysisValidatedForm, + riskAnalysisValidatedFormToNewRiskAnalysisForm, } from "pagopa-interop-commons"; import { eServiceModeNotAllowed, @@ -83,6 +85,46 @@ export function validateRiskAnalysisSchemaOrThrow( } } +export function validateAndTransformRiskAnalysis( + riskAnalysisForm: RiskAnalysisFormSeed | undefined, + tenantKind: TenantKind +): PurposeRiskAnalysisForm | undefined { + if (!riskAnalysisForm) { + return undefined; + } + + const validatedForm = validateRiskAnalysisSchemaOrThrow( + riskAnalysisForm, + tenantKind + ); + + return { + ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), + riskAnalysisId: undefined, + }; +} + +export function reverseValidateAndTransformRiskAnalysis( + riskAnalysisForm: PurposeRiskAnalysisForm | undefined, + tenantKind: TenantKind +): PurposeRiskAnalysisForm | undefined { + if (!riskAnalysisForm) { + return undefined; + } + + const formToValidate = + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm); + const validatedForm = validateRiskAnalysisSchemaOrThrow( + formToValidate, + tenantKind + ); + + return { + ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), + riskAnalysisId: riskAnalysisForm.riskAnalysisId, + }; +} + export function assertTenantKindExists( tenant: Tenant ): asserts tenant is Tenant & { kind: NonNullable } { From 5b7cbe87866c844633616e49d8931046970a3500 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 12:27:24 +0200 Subject: [PATCH 131/537] Simplify data --- .../test/purposeService.integration.test.ts | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 2c948f9af4..84d0054fe1 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -288,14 +288,8 @@ describe("database test", async () => { eserviceId: mockEService.id, versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); expect( purposeService.getRiskAnalysisDocument({ @@ -317,14 +311,8 @@ describe("database test", async () => { eserviceId: mockEService.id, versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -348,14 +336,8 @@ describe("database test", async () => { eserviceId: mockEService.id, versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -383,14 +365,8 @@ describe("database test", async () => { eserviceId: mockEService.id, versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( From 7a1a2b85ba8a5b0af8fc13d342ebba87bfb93d82 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 12:31:32 +0200 Subject: [PATCH 132/537] Simplify test --- .../purpose-process/test/purposeService.integration.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 67a0a49141..6212a4064c 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -471,14 +471,8 @@ describe("database test", async () => { eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( From 6f04c6dcfa73220d6da22de6a7e6507b654e51d8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 12:44:19 +0200 Subject: [PATCH 133/537] Update test --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 6212a4064c..c05153796f 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -503,7 +503,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(randomId), + authData: getMockAuthData(mockEService.producerId), correlationId: generateId(), }) ).rejects.toThrowError(organizationIsNotTheConsumer(randomId)); From a93a0b70307d56a991f8a0d3951a90e61963d401 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 12:49:11 +0200 Subject: [PATCH 134/537] Fix test --- .../purpose-process/test/purposeService.integration.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index c05153796f..396f0c5848 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -489,7 +489,6 @@ describe("database test", async () => { it("Should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); - const randomId: TenantId = generateId(); const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, @@ -506,7 +505,9 @@ describe("database test", async () => { authData: getMockAuthData(mockEService.producerId), correlationId: generateId(), }) - ).rejects.toThrowError(organizationIsNotTheConsumer(randomId)); + ).rejects.toThrowError( + organizationIsNotTheConsumer(mockEService.producerId) + ); }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { const mockEService = getMockEService(); From 4b3d058a1c6cd90f55f2d110b3f39ca0bbdb2ffd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 14:26:34 +0200 Subject: [PATCH 135/537] Implement tests --- .../test/purposeService.integration.test.ts | 235 +++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 6245bb38f8..a53fea9083 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -55,7 +55,9 @@ import { } from "../src/services/readModelService.js"; import { eserviceNotFound, + notValidVersionState, organizationIsNotTheConsumer, + organizationIsNotTheProducer, organizationNotAllowed, purposeNotFound, purposeVersionCannotBeDeleted, @@ -614,9 +616,240 @@ describe("database test", async () => { }); describe("rejectPurposeVersion", () => { - it("test", () => { + it("Should write on event-store for the rejection of a purpose version", () => { expect(3).toBe(3); }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); + }); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError(eserviceNotFound(mockEService.id)); + }); + it("Should throw organizationIsNotTheProducer if the requester is not the producer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockPurpose1.consumerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheProducer(mockPurpose.consumerId) + ); + }); + it("Should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: randomVersionId, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose1.id, randomVersionId) + ); + }); + it("Should throw notValidVersionState if the purpose version is in draft state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("Should throw notValidVersionState if the purpose version is in active state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("Should throw notValidVersionState if the purpose version is in archived state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("Should throw notValidVersionState if the purpose version is in rejected state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("Should throw notValidVersionState if the purpose version is in suspended state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.suspended, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); }); }); }); From ed370144750e73befab71b67e17e65d277ccf3a6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 14:29:56 +0200 Subject: [PATCH 136/537] Minor refactor --- .../test/purposeService.integration.test.ts | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 396f0c5848..7b3caac10c 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -390,16 +390,14 @@ describe("database test", async () => { describe("deletePurposeVersion", () => { it("Should write in event-store for the deletion of a purpose version", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [ - { - ...mockPurposeVersion, - state: purposeVersionState.waitingForApproval, - }, - ], + versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -511,13 +509,14 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [ - { ...mockPurposeVersion, state: purposeVersionState.draft }, - ], + versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -536,16 +535,14 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [ - { - ...mockPurposeVersion, - state: purposeVersionState.active, - }, - ], + versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -564,13 +561,14 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [ - { ...mockPurposeVersion, state: purposeVersionState.archived }, - ], + versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -589,13 +587,14 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.suspended, + }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [ - { ...mockPurposeVersion, state: purposeVersionState.suspended }, - ], + versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); From e92684dfeda0fb3c4e62e4725bed4cafb58f4213 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 14:44:01 +0200 Subject: [PATCH 137/537] Refactor as suggested --- .../src/purpose/protobufConverterFromV1.ts | 12 +++++------ .../src/purpose/protobufConverterToV1.ts | 12 +++++------ packages/models/src/utils.ts | 20 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index 10ceb6ced2..5e77fdbca5 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -7,7 +7,7 @@ import { import { RiskAnalysisId, unsafeBrandId } from "../brandedIds.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; import { PurposeRiskAnalysisFormV1 } from "../gen/v1/purpose/riskAnalysis.js"; -import { bigIntToDate, bigIntToDateOrUndefined } from "../utils.js"; +import { bigIntToDate } from "../utils.js"; import { Purpose, PurposeVersion, @@ -52,14 +52,14 @@ export const fromPurposeVersionV1 = ( ...input, id: unsafeBrandId(input.id), state: fromPurposeVersionStateV1(input.state), - expectedApprovalDate: bigIntToDateOrUndefined(input.expectedApprovalDate), + expectedApprovalDate: bigIntToDate(input.expectedApprovalDate), riskAnalysis: input.riskAnalysis ? fromPurposeVersionDocumentV1(input.riskAnalysis) : undefined, createdAt: bigIntToDate(input.createdAt), - updatedAt: bigIntToDateOrUndefined(input.updatedAt), - firstActivationAt: bigIntToDateOrUndefined(input.firstActivationAt), - suspendedAt: bigIntToDateOrUndefined(input.suspendedAt), + updatedAt: bigIntToDate(input.updatedAt), + firstActivationAt: bigIntToDate(input.firstActivationAt), + suspendedAt: bigIntToDate(input.suspendedAt), }); export const fromPurposeRiskAnalysisFormV1 = ( @@ -88,7 +88,7 @@ export const fromPurposeV1 = (input: PurposeV1): Purpose => ({ versions: input.versions.map(fromPurposeVersionV1), isFreeOfCharge: input.isFreeOfCharge || true, createdAt: bigIntToDate(input.createdAt), - updatedAt: bigIntToDateOrUndefined(input.updatedAt), + updatedAt: bigIntToDate(input.updatedAt), riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV1(input.riskAnalysisForm) : undefined, diff --git a/packages/models/src/purpose/protobufConverterToV1.ts b/packages/models/src/purpose/protobufConverterToV1.ts index 2107bbef65..49b819e5ca 100644 --- a/packages/models/src/purpose/protobufConverterToV1.ts +++ b/packages/models/src/purpose/protobufConverterToV1.ts @@ -6,7 +6,7 @@ import { PurposeVersionV1, } from "../gen/v1/purpose/purpose.js"; -import { dateToBigInt, dateToBigIntOrUndefined } from "../utils.js"; +import { dateToBigInt } from "../utils.js"; import { Purpose, PurposeVersion, @@ -42,11 +42,11 @@ export const toPurposeVersionV1 = ( ): PurposeVersionV1 => ({ ...input, state: toPurposeVersionStateV1(input.state), - expectedApprovalDate: dateToBigIntOrUndefined(input.expectedApprovalDate), + expectedApprovalDate: dateToBigInt(input.expectedApprovalDate), createdAt: dateToBigInt(input.createdAt), - updatedAt: dateToBigIntOrUndefined(input.updatedAt), - firstActivationAt: dateToBigIntOrUndefined(input.firstActivationAt), - suspendedAt: dateToBigIntOrUndefined(input.suspendedAt), + updatedAt: dateToBigInt(input.updatedAt), + firstActivationAt: dateToBigInt(input.firstActivationAt), + suspendedAt: dateToBigInt(input.suspendedAt), riskAnalysis: input.riskAnalysis ? toPurposeVersionDocumentV1(input.riskAnalysis) : undefined, @@ -56,5 +56,5 @@ export const toPurposeV1 = (input: Purpose): PurposeV1 => ({ ...input, versions: input.versions.map(toPurposeVersionV1), createdAt: dateToBigInt(input.createdAt), - updatedAt: dateToBigIntOrUndefined(input.updatedAt), + updatedAt: dateToBigInt(input.updatedAt), }); diff --git a/packages/models/src/utils.ts b/packages/models/src/utils.ts index bcf824c9ec..5c58fa860d 100644 --- a/packages/models/src/utils.ts +++ b/packages/models/src/utils.ts @@ -1,11 +1,11 @@ -export const bigIntToDateOrUndefined = ( - input: bigint | undefined -): Date | undefined => (input ? new Date(Number(input)) : undefined); +export function bigIntToDate(input: bigint): Date; +export function bigIntToDate(input: bigint | undefined): Date | undefined; +export function bigIntToDate(input: bigint | undefined): Date | undefined { + return input ? new Date(Number(input)) : undefined; +} -export const bigIntToDate = (input: bigint): Date => new Date(Number(input)); - -export const dateToBigIntOrUndefined = ( - input: Date | undefined -): bigint | undefined => (input ? BigInt(input.getTime()) : undefined); - -export const dateToBigInt = (input: Date): bigint => BigInt(input.getTime()); +export function dateToBigInt(input: Date): bigint; +export function dateToBigInt(input: Date | undefined): bigint | undefined; +export function dateToBigInt(input: Date | undefined): bigint | undefined { + return input ? BigInt(input.getTime()) : undefined; +} From 955798fe70339eea376f7f3a7ad0290863653d1e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 15:31:24 +0200 Subject: [PATCH 138/537] Minor change in test --- .../test/purposeService.integration.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 7b3caac10c..ae6b585b76 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -33,6 +33,7 @@ import { EServiceId, Purpose, PurposeId, + PurposeVersion, PurposeVersionDocumentId, PurposeVersionId, TenantId, @@ -509,7 +510,7 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; @@ -535,7 +536,7 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.active, }; @@ -561,7 +562,7 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.archived, }; @@ -587,9 +588,10 @@ describe("database test", async () => { }); it("Should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.suspended, + suspendedAt: new Date(), }; const mockPurpose1: Purpose = { ...mockPurpose, From 52fd5e30907192eb5ffdf8fc797524677470fa4c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 15:32:17 +0200 Subject: [PATCH 139/537] Minor change in test --- .../test/purposeService.integration.test.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a2a0aba3ef..ff7962dc63 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -33,6 +33,7 @@ import { EServiceId, Purpose, PurposeId, + PurposeVersion, PurposeVersionDocumentId, PurposeVersionId, TenantId, @@ -717,7 +718,7 @@ describe("database test", async () => { }); it("Should throw notValidVersionState if the purpose version is in draft state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; @@ -744,7 +745,7 @@ describe("database test", async () => { }); it("Should throw notValidVersionState if the purpose version is in active state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.active, }; @@ -771,7 +772,7 @@ describe("database test", async () => { }); it("Should throw notValidVersionState if the purpose version is in archived state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.archived, }; @@ -798,7 +799,7 @@ describe("database test", async () => { }); it("Should throw notValidVersionState if the purpose version is in rejected state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.rejected, }; @@ -825,9 +826,10 @@ describe("database test", async () => { }); it("Should throw notValidVersionState if the purpose version is in suspended state", async () => { const mockEService = getMockEService(); - const mockPurposeVersion = { + const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.suspended, + suspendedAt: new Date(), }; const mockPurpose1: Purpose = { ...mockPurpose, From 0049ec996675a4d0ce893761b640d071f2bc6df5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 15:53:12 +0200 Subject: [PATCH 140/537] Add test --- .../test/purposeService.integration.test.ts | 71 ++++++++++++++++++- 1 file changed, 68 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ff7962dc63..0656537450 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -3,7 +3,15 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from "vitest"; import { EServiceCollection, PurposeCollection, @@ -36,6 +44,7 @@ import { PurposeVersion, PurposeVersionDocumentId, PurposeVersionId, + PurposeVersionRejectedV2, TenantId, TenantKind, WaitingForApprovalPurposeVersionDeletedV2, @@ -617,8 +626,64 @@ describe("database test", async () => { }); describe("rejectPurposeVersion", () => { - it("Should write on event-store for the rejection of a purpose version", () => { - expect(3).toBe(3); + it("Should write on event-store for the rejection of a purpose version", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.rejectPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "PurposeVersionRejected", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionRejectedV2, + payload: writtenEvent.data, + }); + + const expectedPurposeVersion: PurposeVersion = { + ...mockPurposeVersion, + state: purposeVersionState.rejected, + rejectionReason: "test", + updatedAt: new Date(), + }; + const expectedPurpose: Purpose = { + ...mockPurpose1, + versions: [expectedPurposeVersion], + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); }); it("Should throw purposeNotFound if the purpose doesn't exist", async () => { const mockEService = getMockEService(); From 196d443e2b587abe640c2d813c1e89ab8a43a74b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 16:55:26 +0200 Subject: [PATCH 141/537] Add missing event --- packages/models/proto/v2/purpose/events.proto | 6 ++++++ packages/models/src/purpose/purposeEvents.ts | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto index 3e7a919296..1c8391e075 100644 --- a/packages/models/proto/v2/purpose/events.proto +++ b/packages/models/proto/v2/purpose/events.proto @@ -80,3 +80,9 @@ message PurposeVersionRejectedV2 { string versionId = 1; PurposeV2 purpose = 2; } + +message PurposeClonedV2 { + string sourcePurposeId = 1; + string sourceVersionId = 2; + PurposeV2 purpose = 3; +} \ No newline at end of file diff --git a/packages/models/src/purpose/purposeEvents.ts b/packages/models/src/purpose/purposeEvents.ts index d7778608c8..484b4577f6 100644 --- a/packages/models/src/purpose/purposeEvents.ts +++ b/packages/models/src/purpose/purposeEvents.ts @@ -33,6 +33,7 @@ import { PurposeArchivedV2, WaitingForApprovalPurposeVersionDeletedV2, PurposeVersionRejectedV2, + PurposeClonedV2, } from "../gen/v2/purpose/events.js"; export function purposeEventToBinaryData(event: PurposeEvent): Uint8Array { @@ -131,6 +132,9 @@ export function purposeEventToBinaryDataV2(event: PurposeEventV2): Uint8Array { .with({ type: "PurposeVersionRejected" }, ({ data }) => PurposeVersionRejectedV2.toBinary(data) ) + .with({ type: "PurposeCloned" }, ({ data }) => + PurposeClonedV2.toBinary(data) + ) .exhaustive(); } @@ -279,6 +283,11 @@ export const PurposeEventV2 = z.discriminatedUnion("type", [ type: z.literal("PurposeVersionRejected"), data: protobufDecoder(PurposeVersionRejectedV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("PurposeCloned"), + data: protobufDecoder(PurposeClonedV2), + }), ]); export type PurposeEventV2 = z.infer; From d7ce00ff5c791ffc3699deb5894e6fe0989adb3d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 16:57:37 +0200 Subject: [PATCH 142/537] Fix end of file --- packages/models/proto/v2/purpose/events.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto index 1c8391e075..d38f3672e3 100644 --- a/packages/models/proto/v2/purpose/events.proto +++ b/packages/models/proto/v2/purpose/events.proto @@ -85,4 +85,4 @@ message PurposeClonedV2 { string sourcePurposeId = 1; string sourceVersionId = 2; PurposeV2 purpose = 3; -} \ No newline at end of file +} From e492a1886026b5999ce9d42336eec591f2597efe Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:28:25 +0200 Subject: [PATCH 143/537] Add mapper --- packages/purpose-process/src/model/domain/errors.ts | 12 ++++++++++++ .../purpose-process/src/utilities/errorMappers.ts | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a772a16f57..a76da0a02b 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -27,6 +27,7 @@ export const errorCodes = { riskAnalysisValidationFailed: "0013", purposeNotInDraftState: "0014", notValidVersionState: "0015", + purposeCannotBeDeleted: "0016", }; export type ErrorCodes = keyof typeof errorCodes; @@ -167,6 +168,7 @@ export function purposeNotInDraftState( title: "Purpose not in draft state", }); } + export function notValidVersionState( purposeVersionId: PurposeVersionId, versionState: PurposeVersionState @@ -177,3 +179,13 @@ export function notValidVersionState( title: "Not valid purpose version state", }); } + +export function purposeCannotBeDeleted( + purposeId: PurposeId +): ApiError { + return new ApiError({ + detail: `Purpose ${purposeId} cannot be deleted`, + code: "purposeCannotBeDeleted", + title: "Purpose canont be deleted", + }); +} diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 91f1402a22..5516150a76 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -64,3 +64,9 @@ export const updatePurposeErrorMapper = (error: ApiError): number => .with("eserviceNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const deletePurposeErrorMapper = (error: ApiError): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("purposeCannotBeDeleted", () => HTTP_STATUS_CONFLICT) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 5810ba53e2bca36853e39b0eabde904c40f7fa96 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:52:54 +0200 Subject: [PATCH 144/537] Add event converter --- .../src/model/domain/toEvent.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index db4c98cc6a..a5d9a43914 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -68,3 +68,41 @@ export const toCreateEventDraftPurposeUpdated = ({ }, correlationId, }); + +export const toCreateEventDraftPurposeDeleted = ({ + purpose, + version, + correlationId, +}: { + purpose: Purpose; + version: number; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "DraftPurposeDeleted", + event_version: 2, + data: { purpose: toPurposeV2(purpose) }, + }, + correlationId, +}); + +export const toCreateEventWaintingForApprovalPurposeDeleted = ({ + purpose, + version, + correlationId, +}: { + purpose: Purpose; + version: number; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "WaitingForApprovalPurposeDeleted", + event_version: 2, + data: { purpose: toPurposeV2(purpose) }, + }, + correlationId, +}); From 4949bf649a474228929464d9a4327b1e211908c2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:53:02 +0200 Subject: [PATCH 145/537] Update mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 5516150a76..414db45aa8 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -68,5 +68,6 @@ export const updatePurposeErrorMapper = (error: ApiError): number => export const deletePurposeErrorMapper = (error: ApiError): number => match(error.code) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("purposeCannotBeDeleted", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From fc3b28e6ee02c523be4d15008990dc35a5c7d941 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:53:21 +0200 Subject: [PATCH 146/537] Add check --- packages/purpose-process/src/services/validators.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index d190ea3cf9..fcc4e56139 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -19,6 +19,7 @@ import { eServiceModeNotAllowed, missingFreeOfChargeReason, organizationIsNotTheConsumer, + purposeCannotBeDeleted, purposeNotInDraftState, riskAnalysisValidationFailed, tenantKindNotFound, @@ -138,3 +139,15 @@ export function assertIsDraft(purpose: Purpose): void { throw purposeNotInDraftState(purpose.id); } } + +export function assertPurposeIsDeletable(purpose: Purpose): void { + if ( + purpose.versions.some( + (v) => + v.state !== purposeVersionState.draft && + v.state !== purposeVersionState.waitingForApproval + ) + ) { + throw purposeCannotBeDeleted(purpose.id); + } +} From d7f96e55bf4e3919d8937af62c3e63bd600adf4e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:53:31 +0200 Subject: [PATCH 147/537] Implement endpoint --- .../src/routers/PurposeRouter.ts | 50 +++++-------------- .../src/services/purposeService.ts | 32 ++++++++++++ 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index d19d466ead..527e28c1e6 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -19,11 +19,11 @@ import { config } from "../utilities/config.js"; import { purposeServiceBuilder } from "../services/purposeService.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { + deletePurposeErrorMapper, deletePurposeVersionErrorMapper, getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, - updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( @@ -79,24 +79,7 @@ const purposeRouter = ( .post( "/reverse/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), - async (req, res) => { - try { - const { purpose, isRiskAnalysisValid } = - await purposeService.updateReversePurpose({ - purposeId: unsafeBrandId(req.params.id), - reversePurposeUpdateContent: req.body, - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - }); - return res - .status(200) - .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) - .end(); - } catch (error) { - const errorRes = makeApiProblem(error, updatePurposeErrorMapper); - return res.status(errorRes.status).json(errorRes).end(); - } - } + (_req, res) => res.status(501).send() ) .get( "/purposes/:id", @@ -124,33 +107,26 @@ const purposeRouter = ( } } ) - .post( + .post("/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => + res.status(501).send() + ) + .delete( "/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { try { - const { purpose, isRiskAnalysisValid } = - await purposeService.updatePurpose({ - purposeId: unsafeBrandId(req.params.id), - purposeUpdateContent: req.body, - organizationId: req.ctx.authData.organizationId, - correlationId: req.ctx.correlationId, - }); - return res - .status(200) - .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) - .end(); + await purposeService.deletePurpose({ + purposeId: unsafeBrandId(req.params.id), + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res.status(204).end(); } catch (error) { - const errorRes = makeApiProblem(error, updatePurposeErrorMapper); + const errorRes = makeApiProblem(error, deletePurposeErrorMapper); return res.status(errorRes.status).json(errorRes).end(); } } ) - .delete( - "/purposes/:id", - authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() - ) .post( "/purposes/:purposeId/versions", authorizationMiddleware([ADMIN_ROLE]), diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 4e070ab0b9..dda642daad 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -37,8 +37,10 @@ import { tenantNotFound, } from "../model/domain/errors.js"; import { + toCreateEventDraftPurposeDeleted, toCreateEventDraftPurposeUpdated, toCreateEventPurposeVersionRejected, + toCreateEventWaintingForApprovalPurposeDeleted, toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { @@ -56,6 +58,7 @@ import { assertIsDraft, reverseValidateAndTransformRiskAnalysis, validateAndTransformRiskAnalysis, + assertPurposeIsDeletable, } from "./validators.js"; const retrievePurpose = async ( @@ -310,6 +313,35 @@ export function purposeServiceBuilder( { readModelService, correlationId, repository } ); }, + async deletePurpose({ + purposeId, + organizationId, + correlationId, + }: { + purposeId: PurposeId; + organizationId: TenantId; + correlationId: string; + }): Promise { + const purpose = await retrievePurpose(purposeId, readModelService); + + assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); + + assertPurposeIsDeletable(purpose.data); + + const event = purposeIsDraft(purpose.data) + ? toCreateEventDraftPurposeDeleted({ + purpose: purpose.data, + version: purpose.metadata.version, + correlationId, + }) + : toCreateEventWaintingForApprovalPurposeDeleted({ + purpose: purpose.data, + version: purpose.metadata.version, + correlationId, + }); + + await repository.createEvent(event); + }, }; } From 94a765a2c2a572f8c31a6fb998f4d9f4ce4cadc0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:55:33 +0200 Subject: [PATCH 148/537] Revert unintended deletion --- .../src/routers/PurposeRouter.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 527e28c1e6..e57668817e 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -24,6 +24,7 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, + updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( @@ -79,7 +80,24 @@ const purposeRouter = ( .post( "/reverse/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.updateReversePurpose({ + purposeId: unsafeBrandId(req.params.id), + reversePurposeUpdateContent: req.body, + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, updatePurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .get( "/purposes/:id", From 85c9780af47738165e49b139e9d708b1ed32bc80 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:57:29 +0200 Subject: [PATCH 149/537] Fix --- .../src/routers/PurposeRouter.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index e57668817e..f2dce80b60 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -125,8 +125,27 @@ const purposeRouter = ( } } ) - .post("/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => - res.status(501).send() + .post( + "/purposes/:id", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.updateReversePurpose({ + purposeId: unsafeBrandId(req.params.id), + reversePurposeUpdateContent: req.body, + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, updatePurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .delete( "/purposes/:id", From 6914f3074710235609eeb3a34e88563b39ea8659 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 17:58:18 +0200 Subject: [PATCH 150/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index f2dce80b60..d9ee4460d0 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -131,9 +131,9 @@ const purposeRouter = ( async (req, res) => { try { const { purpose, isRiskAnalysisValid } = - await purposeService.updateReversePurpose({ + await purposeService.updatePurpose({ purposeId: unsafeBrandId(req.params.id), - reversePurposeUpdateContent: req.body, + purposeUpdateContent: req.body, organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, }); From c34e133b45211775a7d9e84e493f5c0bb8dcb62e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 18:01:52 +0200 Subject: [PATCH 151/537] Minor case --- .../test/purposeService.integration.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index bb89a787c9..b2c17e8bab 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -103,7 +103,7 @@ describe("database test", async () => { describe("Purpose service", () => { const mockPurpose = getMockPurpose(); describe("getPurposeById", () => { - it("Should get the purpose if it exists", async () => { + it("should get the purpose if it exists", async () => { const mockEService = getMockEService(); const mockTenant = { ...getMockTenant(), @@ -133,7 +133,7 @@ describe("database test", async () => { isRiskAnalysisValid: false, }); }); - it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + it("should throw purposeNotFound if the purpose doesn't exist", async () => { const notExistingId: PurposeId = generateId(); await addOnePurpose(mockPurpose, postgresDB, purposes); @@ -141,7 +141,7 @@ describe("database test", async () => { purposeService.getPurposeById(notExistingId, getMockAuthData()) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); - it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + it("should throw eserviceNotFound if the eservice doesn't exist", async () => { const notExistingId: EServiceId = generateId(); const mockTenant = { ...getMockTenant(), @@ -168,7 +168,7 @@ describe("database test", async () => { ) ).rejects.toThrowError(eserviceNotFound(notExistingId)); }); - it("Should throw tenantNotFound if the tenant doesn't exist", async () => { + it("should throw tenantNotFound if the tenant doesn't exist", async () => { const mockEService = getMockEService(); const notExistingId: TenantId = generateId(); @@ -192,7 +192,7 @@ describe("database test", async () => { ) ).rejects.toThrowError(tenantNotFound(notExistingId)); }); - it("Should throw tenantKindNotFound if the tenant doesn't exist", async () => { + it("should throw tenantKindNotFound if the tenant doesn't exist", async () => { const mockEService = getMockEService(); const mockTenant = getMockTenant(); From 2931abb12e0b2ef43c30efec1ee374eeeb10172f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 18:02:33 +0200 Subject: [PATCH 152/537] Minor case --- .../test/purposeService.integration.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 84d0054fe1..f9371e9fc7 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -227,7 +227,7 @@ describe("database test", async () => { }); describe("getRiskAnalysisDocument", () => { - it("Should get the purpose version document", async () => { + it("should get the purpose version document", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); @@ -253,7 +253,7 @@ describe("database test", async () => { }); expect(result).toEqual(mockDocument); }); - it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + it("should throw purposeNotFound if the purpose doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); @@ -279,7 +279,7 @@ describe("database test", async () => { }) ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); }); - it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + it("should throw eserviceNotFound if the eservice doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); @@ -300,7 +300,7 @@ describe("database test", async () => { }) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); - it("Should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const randomVersionId: PurposeVersionId = generateId(); @@ -326,7 +326,7 @@ describe("database test", async () => { purposeVersionNotFound(mockPurpose1.id, randomVersionId) ); }); - it("Should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { + it("should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); @@ -355,7 +355,7 @@ describe("database test", async () => { ) ); }); - it("Should throw organizationNotAllowed if the requester is not the producer not the consumer", async () => { + it("should throw organizationNotAllowed if the requester is not the producer not the consumer", async () => { const randomId: TenantId = generateId(); const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); From a738c8570402039e71c0356e08436de3ec02ac27 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 18:03:13 +0200 Subject: [PATCH 153/537] Minor case --- .../test/purposeService.integration.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ae6b585b76..a87be1e184 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -389,7 +389,7 @@ describe("database test", async () => { }); describe("deletePurposeVersion", () => { - it("Should write in event-store for the deletion of a purpose version", async () => { + it("should write in event-store for the deletion of a purpose version", async () => { const mockEService = getMockEService(); const mockPurposeVersion = { ...getMockPurposeVersion(), @@ -436,7 +436,7 @@ describe("database test", async () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); - it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + it("should throw purposeNotFound if the purpose doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose1: Purpose = { @@ -461,7 +461,7 @@ describe("database test", async () => { }) ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); }); - it("Should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const randomVersionId: PurposeVersionId = generateId(); @@ -485,7 +485,7 @@ describe("database test", async () => { purposeVersionNotFound(mockPurpose1.id, randomVersionId) ); }); - it("Should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose1: Purpose = { @@ -508,7 +508,7 @@ describe("database test", async () => { organizationIsNotTheConsumer(mockEService.producerId) ); }); - it("Should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { + it("should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -534,7 +534,7 @@ describe("database test", async () => { purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) ); }); - it("Should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { + it("should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -560,7 +560,7 @@ describe("database test", async () => { purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) ); }); - it("Should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { + it("should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -586,7 +586,7 @@ describe("database test", async () => { purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) ); }); - it("Should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { + it("should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), From b0d8cc6adbf6ded031ba63a8ab8f083e5f13cd18 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 18:04:42 +0200 Subject: [PATCH 154/537] Minor case --- .../test/purposeService.integration.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 0656537450..5582dcbac3 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -626,7 +626,7 @@ describe("database test", async () => { }); describe("rejectPurposeVersion", () => { - it("Should write on event-store for the rejection of a purpose version", async () => { + it("should write on event-store for the rejection of a purpose version", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); @@ -685,7 +685,7 @@ describe("database test", async () => { vi.useRealTimers(); }); - it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + it("should throw purposeNotFound if the purpose doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose1: Purpose = { @@ -732,7 +732,7 @@ describe("database test", async () => { }) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); - it("Should throw organizationIsNotTheProducer if the requester is not the producer", async () => { + it("should throw organizationIsNotTheProducer if the requester is not the producer", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose1: Purpose = { @@ -756,7 +756,7 @@ describe("database test", async () => { organizationIsNotTheProducer(mockPurpose.consumerId) ); }); - it("Should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const randomVersionId: PurposeVersionId = generateId(); @@ -781,7 +781,7 @@ describe("database test", async () => { purposeVersionNotFound(mockPurpose1.id, randomVersionId) ); }); - it("Should throw notValidVersionState if the purpose version is in draft state", async () => { + it("should throw notValidVersionState if the purpose version is in draft state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -808,7 +808,7 @@ describe("database test", async () => { notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); }); - it("Should throw notValidVersionState if the purpose version is in active state", async () => { + it("should throw notValidVersionState if the purpose version is in active state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -835,7 +835,7 @@ describe("database test", async () => { notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); }); - it("Should throw notValidVersionState if the purpose version is in archived state", async () => { + it("should throw notValidVersionState if the purpose version is in archived state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -862,7 +862,7 @@ describe("database test", async () => { notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); }); - it("Should throw notValidVersionState if the purpose version is in rejected state", async () => { + it("should throw notValidVersionState if the purpose version is in rejected state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -889,7 +889,7 @@ describe("database test", async () => { notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); }); - it("Should throw notValidVersionState if the purpose version is in suspended state", async () => { + it("should throw notValidVersionState if the purpose version is in suspended state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), From 2612bc05799a5dfff6119ea1fce00e9e78edd445 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 15 Apr 2024 18:10:31 +0200 Subject: [PATCH 155/537] Add test scaffold --- .../test/purposeService.integration.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 0656537450..0db71f39ad 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -918,5 +918,32 @@ describe("database test", async () => { ); }); }); + + describe("deletePurpose", () => { + it("should write on event-store for the deletion of a purpose (no versions)", () => { + expect(1).toBe(1); + }); + it("should write on event-store for the deletion of a purpose (draft version)", () => { + expect(1).toBe(1); + }); + it("should write on event-store for the deletion of a purpose (waiting for approval version)", () => { + expect(1).toBe(1); + }); + it("should throw organizationIsNotAConsumer if the requester is not the consumer", () => { + expect(1).toBe(1); + }); + it("should throw purposeCannotBeDeleted if the purpose has an active version ", () => { + expect(1).toBe(1); + }); + it("should throw purposeCannotBeDeleted if the purpose has a rejected version ", () => { + expect(1).toBe(1); + }); + it("should throw purposeCannotBeDeleted if the purpose has a suspeneded version ", () => { + expect(1).toBe(1); + }); + it("should throw purposeCannotBeDeleted if the purpose has an archived version ", () => { + expect(1).toBe(1); + }); + }); }); }); From ebd54fab89b57485526698b977d46f7525f81483 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 15 Apr 2024 18:43:26 +0200 Subject: [PATCH 156/537] fixing some problems --- .../purpose-process/src/services/purposeService.ts | 6 +++--- packages/purpose-process/src/services/validators.ts | 12 ++++++------ packages/purpose-process/test/utils.ts | 12 +++++++++++- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 4e070ab0b9..957fb5273d 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -53,9 +53,9 @@ import { isRiskAnalysisFormValid, purposeIsDraft, assertTenantKindExists, - assertIsDraft, reverseValidateAndTransformRiskAnalysis, validateAndTransformRiskAnalysis, + assertPurposeIsDraft, } from "./validators.js"; const retrievePurpose = async ( @@ -410,13 +410,13 @@ const updatePurposeInternal = async ( ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { const purpose = await retrievePurpose(purposeId, readModelService); assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); - assertIsDraft(purpose.data); + assertPurposeIsDraft(purpose.data); const eservice = await retrieveEService( purpose.data.eserviceId, readModelService ); - isEserviceMode(eservice.id, eserviceMode); + isEserviceMode(eservice, eserviceMode); isFreeOfCharge( updateContent.isFreeOfCharge, updateContent.freeOfChargeReason diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index d190ea3cf9..8e056cf789 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -1,5 +1,5 @@ import { - EServiceId, + EService, EServiceMode, Purpose, PurposeRiskAnalysisForm, @@ -47,11 +47,11 @@ export const purposeIsDraft = (purpose: Purpose): boolean => !purpose.versions.some((v) => v.state !== purposeVersionState.draft); export const isEserviceMode = ( - eserviceId: EServiceId, - mode: EServiceMode + eservice: EService, + expectedMode: EServiceMode ): void => { - if (mode !== mode) { - throw eServiceModeNotAllowed(eserviceId, mode); + if (eservice.mode !== expectedMode) { + throw eServiceModeNotAllowed(eservice.id, expectedMode); } }; @@ -133,7 +133,7 @@ export function assertTenantKindExists( } } -export function assertIsDraft(purpose: Purpose): void { +export function assertPurposeIsDraft(purpose: Purpose): void { if (!purposeIsDraft(purpose)) { throw purposeNotInDraftState(purpose.id); } diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 885b604305..88ea2169e2 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -1,4 +1,7 @@ -import { PurposeCollection } from "pagopa-interop-commons"; +import { + PurposeCollection, + riskAnalysisFormToRiskAnalysisFormToValidate, +} from "pagopa-interop-commons"; import { writeInEventstore, writeInReadmodel, @@ -7,12 +10,14 @@ import { EService, Purpose, PurposeEvent, + RiskAnalysis, generateId, purposeEventToBinaryData, technology, toPurposeV2, } from "pagopa-interop-models"; import { IDatabase } from "pg-promise"; +import { RiskAnalysisFormSeed } from "../src/model/domain/models.js"; export const addOnePurpose = async ( purpose: Purpose, @@ -56,3 +61,8 @@ export const getMockEService = (): EService => ({ riskAnalysis: [], mode: "Deliver", }); + +export const buildRiskAnalysisSeed = ( + riskAnalysis: RiskAnalysis +): RiskAnalysisFormSeed => + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysis.riskAnalysisForm); From 5a0338c58aa53aee2e4a654b0f90578e7800a109 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 15 Apr 2024 18:43:43 +0200 Subject: [PATCH 157/537] initial testing --- .../test/purposeService.integration.test.ts | 228 +++++++++++++++++- 1 file changed, 225 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ea9880da67..91501ca938 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -20,7 +20,6 @@ import { initDB, } from "pagopa-interop-commons"; import { IDatabase } from "pg-promise"; - import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, @@ -30,13 +29,16 @@ import { getMockPurposeVersion, getMockPurposeVersionDocument, getMockTenant, + getMockValidRiskAnalysis, mongoDBContainer, postgreSQLContainer, + randomArrayItem, readLastEventByStreamId, writeInReadmodel, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; import { + DraftPurposeUpdatedV2, EService, EServiceId, Purpose, @@ -45,14 +47,15 @@ import { PurposeVersionDocumentId, PurposeVersionId, PurposeVersionRejectedV2, + Tenant, TenantId, - TenantKind, WaitingForApprovalPurposeVersionDeletedV2, generateId, purposeVersionState, tenantKind, toPurposeV2, toReadModelEService, + unsafeBrandId, } from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { @@ -76,7 +79,15 @@ import { tenantKindNotFound, tenantNotFound, } from "../src/model/domain/errors.js"; -import { addOnePurpose, getMockEService } from "./utils.js"; +import { + PurposeUpdateContent, + ReversePurposeUpdateContent, +} from "../src/model/domain/models.js"; +import { + addOnePurpose, + buildRiskAnalysisSeed, + getMockEService, +} from "./utils.js"; describe("database test", async () => { let purposes: PurposeCollection; @@ -920,4 +931,215 @@ describe("database test", async () => { }); }); }); + describe("updatePurpose and reverseUpdatePurpose", () => { + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + const consumerTenantKind = randomArrayItem(Object.values(tenantKind)); + const consumer: Tenant = { + ...getMockTenant(), + kind: consumerTenantKind, + }; + + const mockEservice: EService = { ...getMockEService(), mode: "Deliver" }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEservice.id, + consumerId: consumer.id, + versions: [ + { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }, + ], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(consumer, tenants); + + const mockValidRiskAnalysis = + getMockValidRiskAnalysis(consumerTenantKind); + + const purposeUpdateContent: PurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + riskAnalysisForm: buildRiskAnalysisSeed(mockValidRiskAnalysis), + }; + + await purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: consumer.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + title: purposeUpdateContent.title, + description: purposeUpdateContent.description, + isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + }, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + const producerTenantKind = randomArrayItem(Object.values(tenantKind)); + const producer: Tenant = { + ...getMockTenant(), + kind: producerTenantKind, + }; + + const mockEservice: EService = { + ...getMockEService(), + mode: "Receive", + producerId: producer.id, + }; + + const mockValidRiskAnalysis = + getMockValidRiskAnalysis(producerTenantKind); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEservice.id, + consumerId: producer.id, + versions: [ + { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }, + ], + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: generateId(), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: generateId(), + }) + ), + multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: generateId(), + }) + ), + }, + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(producer, tenants); + + const reversePurposeUpdateContent: ReversePurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + }; + + await purposeService.updateReversePurpose({ + purposeId: mockPurpose.id, + reversePurposeUpdateContent, + organizationId: producer.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + title: reversePurposeUpdateContent.title, + description: reversePurposeUpdateContent.description, + isFreeOfCharge: reversePurposeUpdateContent.isFreeOfCharge, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + }, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + }); }); From 94f7d88d74022743584c67098e1401b572f739ac Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 09:16:46 +0200 Subject: [PATCH 158/537] Fix typo --- packages/purpose-process/src/model/domain/toEvent.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index a5d9a43914..9bf37a6ca5 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -88,7 +88,7 @@ export const toCreateEventDraftPurposeDeleted = ({ correlationId, }); -export const toCreateEventWaintingForApprovalPurposeDeleted = ({ +export const toCreateEventWaitingForApprovalPurposeDeleted = ({ purpose, version, correlationId, diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index dda642daad..01f3a1c001 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -40,7 +40,7 @@ import { toCreateEventDraftPurposeDeleted, toCreateEventDraftPurposeUpdated, toCreateEventPurposeVersionRejected, - toCreateEventWaintingForApprovalPurposeDeleted, + toCreateEventWaitingForApprovalPurposeDeleted, toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { @@ -334,7 +334,7 @@ export function purposeServiceBuilder( version: purpose.metadata.version, correlationId, }) - : toCreateEventWaintingForApprovalPurposeDeleted({ + : toCreateEventWaitingForApprovalPurposeDeleted({ purpose: purpose.data, version: purpose.metadata.version, correlationId, From 4eadedb4e3e77b2cf4021f64368a31a466484544 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 09:31:15 +0200 Subject: [PATCH 159/537] Add test scaffold --- .../test/purposeService.integration.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 0db71f39ad..9fd5ad7cb0 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -945,5 +945,12 @@ describe("database test", async () => { expect(1).toBe(1); }); }); + + describe("archivePurpose", () => { + it("should write on event-store for the archiving of a purpose", () => {}); + it("should throw purposeNotFound if the purpose doesn't exist", () => {}); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", () => {}); + it("should throw organizationIsNotAConsumer if the requester is not the consumer", () => {}); + }); }); }); From 0b2bcd03a521bdbaad3dd18dee249e450fa640c5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 09:59:49 +0200 Subject: [PATCH 160/537] Add event converter --- .../src/model/domain/toEvent.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index 9bf37a6ca5..20338304f0 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -106,3 +106,22 @@ export const toCreateEventWaitingForApprovalPurposeDeleted = ({ }, correlationId, }); + +export const toCreateEventPurposeArchived = ({ + purpose, + version, + correlationId, +}: { + purpose: Purpose; + version: number; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "PurposeArchived", + event_version: 2, + data: { purpose: toPurposeV2(purpose) }, + }, + correlationId, +}); From cb2d5bdaf101dc9bdedcd0c889b39ed4afb0e86c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 09:59:56 +0200 Subject: [PATCH 161/537] Add business logic --- .../src/services/purposeService.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 01f3a1c001..5477fabd45 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -39,6 +39,7 @@ import { import { toCreateEventDraftPurposeDeleted, toCreateEventDraftPurposeUpdated, + toCreateEventPurposeArchived, toCreateEventPurposeVersionRejected, toCreateEventWaitingForApprovalPurposeDeleted, toCreateEventWaitingForApprovalPurposeVersionDeleted, @@ -342,6 +343,54 @@ export function purposeServiceBuilder( await repository.createEvent(event); }, + async archivePurposeVersion({ + purposeId, + versionId, + organizationId, + correlationId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + organizationId: TenantId; + correlationId: string; + }): Promise { + const purpose = await retrievePurpose(purposeId, readModelService); + + assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); + const purposeVersion = retrievePurposeVersion(versionId, purpose); + + if ( + purposeVersion.state !== purposeVersionState.active && + purposeVersion.state !== purposeVersionState.suspended + ) { + throw notValidVersionState(versionId, purposeVersion.state); + } + + const purposeWithoutWaiting: Purpose = { + ...purpose.data, + versions: purpose.data.versions.filter( + (v) => v.state !== purposeVersionState.waitingForApproval + ), + }; + const archivedVersion: PurposeVersion = { + ...purposeVersion, + state: purposeVersionState.rejected, + updatedAt: new Date(), + }; + const updatedPurpose = replacePurposeVersion( + purposeWithoutWaiting, + archivedVersion + ); + + const event = toCreateEventPurposeArchived({ + purpose: updatedPurpose, + version: purpose.metadata.version, + correlationId, + }); + + await repository.createEvent(event); + return archivedVersion; + }, }; } From 1c53ab126aa4a46d8f034e35e34d0733dc12bd10 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:23:44 +0200 Subject: [PATCH 162/537] Add test placeholders --- .../test/purposeService.integration.test.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 9fd5ad7cb0..561b6e7460 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -947,10 +947,18 @@ describe("database test", async () => { }); describe("archivePurpose", () => { - it("should write on event-store for the archiving of a purpose", () => {}); - it("should throw purposeNotFound if the purpose doesn't exist", () => {}); - it("should throw purposeVersionNotFound if the purpose version doesn't exist", () => {}); - it("should throw organizationIsNotAConsumer if the requester is not the consumer", () => {}); + it("should write on event-store for the archiving of a purpose", () => { + expect(1).toBe(1); + }); + it("should throw purposeNotFound if the purpose doesn't exist", () => { + expect(1).toBe(1); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", () => { + expect(1).toBe(1); + }); + it("should throw organizationIsNotAConsumer if the requester is not the consumer", () => { + expect(1).toBe(1); + }); }); }); }); From 87d80dd560b2f21f98aebb4f8eaa838c3d9eacb3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:23:52 +0200 Subject: [PATCH 163/537] Update router --- .../src/routers/PurposeRouter.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index d9ee4460d0..1c49d6a6b3 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -13,6 +13,7 @@ import { api } from "../model/generated/api.js"; import { purposeToApiPurpose, purposeVersionDocumentToApiPurposeVersionDocument, + purposeVersionToApiPurposeVersion, } from "../model/domain/apiConverter.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../utilities/config.js"; @@ -254,7 +255,26 @@ const purposeRouter = ( .post( "/purposes/:purposeId/versions/:versionId/archive", authorizationMiddleware([ADMIN_ROLE, INTERNAL_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const archivedVersion = await purposeService.archivePurposeVersion({ + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res + .status(200) + .json(purposeVersionToApiPurposeVersion(archivedVersion)) + .end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + rejectPurposeVersionErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/purposes/:purposeId/versions/:versionId/update/waitingForApproval", From c9637a46092276790ce5eac1e16d74daf617c041 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:29:14 +0200 Subject: [PATCH 164/537] Add test placeholder --- .../test/purposeService.integration.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 561b6e7460..a9fc04d78e 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -960,5 +960,17 @@ describe("database test", async () => { expect(1).toBe(1); }); }); + + describe("suspendPurposeVersion", () => { + it("should write on event-store for the suspension of a purpose", () => { + expect(1).toBe(1); + }); + it("should throw purposeNotFound if the purpose doesn't exist", () => { + expect(1).toBe(1); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", () => { + expect(1).toBe(1); + }); + }); }); }); From 9b789ff5d482092d023a2dfc9b99df86bfb49e6f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:53:47 +0200 Subject: [PATCH 165/537] Fix --- packages/models/src/purpose/purpose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 9190128be2..feecc4e13c 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -70,4 +70,4 @@ export const Ownership = z.enum([ Object.values(ownership)[0], ...Object.values(ownership).slice(1), ]); -export type Ownership = z.infer; +export type Ownership = z.infer; From 082035b873b23f4ec48c9d8afe3f0529c496f724 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:53:59 +0200 Subject: [PATCH 166/537] Add event converter --- .../src/model/domain/toEvent.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index 20338304f0..5d2d5dbcac 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -125,3 +125,45 @@ export const toCreateEventPurposeArchived = ({ }, correlationId, }); + +export const toCreateEventPurposeSuspendedByConsumer = ({ + purpose, + purposeVersionId, + version, + correlationId, +}: { + purpose: Purpose; + purposeVersionId: PurposeVersionId; + version: number; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "PurposeVersionSuspendedByConsumer", + event_version: 2, + data: { purpose: toPurposeV2(purpose), versionId: purposeVersionId }, + }, + correlationId, +}); + +export const toCreateEventPurposeSuspendedByProducer = ({ + purpose, + purposeVersionId, + version, + correlationId, +}: { + purpose: Purpose; + purposeVersionId: PurposeVersionId; + version: number; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version, + event: { + type: "PurposeVersionSuspendedByProducer", + event_version: 2, + data: { purpose: toPurposeV2(purpose), versionId: purposeVersionId }, + }, + correlationId, +}); From 7dfc4026c21082d2d2e78af1abb8945173a0fc33 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:54:10 +0200 Subject: [PATCH 167/537] Implement endpoint --- .../src/services/purposeService.ts | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 5477fabd45..ea972776a8 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -24,7 +24,9 @@ import { PurposeRiskAnalysisForm, PurposeEvent, EServiceMode, + Ownership, } from "pagopa-interop-models"; +import { match } from "ts-pattern"; import { eserviceNotFound, notValidVersionState, @@ -40,6 +42,8 @@ import { toCreateEventDraftPurposeDeleted, toCreateEventDraftPurposeUpdated, toCreateEventPurposeArchived, + toCreateEventPurposeSuspendedByConsumer, + toCreateEventPurposeSuspendedByProducer, toCreateEventPurposeVersionRejected, toCreateEventWaitingForApprovalPurposeDeleted, toCreateEventWaitingForApprovalPurposeVersionDeleted, @@ -391,6 +395,82 @@ export function purposeServiceBuilder( await repository.createEvent(event); return archivedVersion; }, + + async suspendPurposeVersion({ + purposeId, + versionId, + organizationId, + correlationId, + }: { + purposeId: PurposeId; + versionId: PurposeVersionId; + organizationId: TenantId; + correlationId: string; + }): Promise { + const purpose = await retrievePurpose(purposeId, readModelService); + + const eservice = await retrieveEService( + purpose.data.eserviceId, + readModelService + ); + + const suspender = getOrganizationRole({ + organizationId, + producerId: eservice.producerId, + consumerId: purpose.data.consumerId, + }); + + const purposeVersion = retrievePurposeVersion(versionId, purpose); + + const suspendedPurposeVersion: PurposeVersion = { + ...purposeVersion, + state: purposeVersionState.suspended, + suspendedAt: new Date(), + }; + + const event = match(suspender) + .with(ownership.CONSUMER, () => { + const updatedPurpose: Purpose = { + ...replacePurposeVersion(purpose.data, suspendedPurposeVersion), + suspendedByConsumer: true, + }; + return toCreateEventPurposeSuspendedByConsumer({ + purpose: updatedPurpose, + purposeVersionId: versionId, + version: purpose.metadata.version, + correlationId, + }); + }) + .with(ownership.PRODUCER, () => { + const updatedPurpose: Purpose = { + ...replacePurposeVersion(purpose.data, suspendedPurposeVersion), + suspendedByProducer: true, + }; + return toCreateEventPurposeSuspendedByProducer({ + purpose: updatedPurpose, + purposeVersionId: versionId, + version: purpose.metadata.version, + correlationId, + }); + }) + .with(ownership.SELF_CONSUMER, () => { + const updatedPurpose: Purpose = { + ...replacePurposeVersion(purpose.data, suspendedPurposeVersion), + suspendedByConsumer: true, + suspendedByProducer: true, + }; + return toCreateEventPurposeSuspendedByConsumer({ + purpose: updatedPurpose, + purposeVersionId: versionId, + version: purpose.metadata.version, + correlationId, + }); + }) + .exhaustive(); + + await repository.createEvent(event); + return suspendedPurposeVersion; + }, }; } @@ -434,7 +514,7 @@ const getOrganizationRole = ({ organizationId: TenantId; producerId: TenantId; consumerId: TenantId; -}): string => { +}): Ownership => { if (producerId === consumerId && organizationId === producerId) { return ownership.SELF_CONSUMER; } else if (producerId !== consumerId && organizationId === consumerId) { From 2299b7715840f35d8cc24850a0994f2bf68010db Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 10:55:31 +0200 Subject: [PATCH 168/537] Fix ownership type --- packages/models/src/purpose/purpose.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 9190128be2..feecc4e13c 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -70,4 +70,4 @@ export const Ownership = z.enum([ Object.values(ownership)[0], ...Object.values(ownership).slice(1), ]); -export type Ownership = z.infer; +export type Ownership = z.infer; diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 6be1951f01..a9b426ce68 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -21,6 +21,7 @@ import { PurposeVersion, PurposeVersionDocument, ownership, + Ownership, } from "pagopa-interop-models"; import { eserviceNotFound, @@ -223,7 +224,7 @@ const getOrganizationRole = ({ organizationId: TenantId; producerId: TenantId; consumerId: TenantId; -}): string => { +}): Ownership => { if (producerId === consumerId && organizationId === producerId) { return ownership.SELF_CONSUMER; } else if (producerId !== consumerId && organizationId === consumerId) { From 2c7b51d2c5af13309f0531f77d5bd304be3a73e1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 11:26:41 +0200 Subject: [PATCH 169/537] Add error mapper --- packages/purpose-process/src/routers/PurposeRouter.ts | 3 ++- packages/purpose-process/src/utilities/errorMappers.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 1c49d6a6b3..c16c4024f2 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -20,6 +20,7 @@ import { config } from "../utilities/config.js"; import { purposeServiceBuilder } from "../services/purposeService.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { + archivePurposeVersionErrorMapper, deletePurposeErrorMapper, deletePurposeVersionErrorMapper, getPurposeErrorMapper, @@ -270,7 +271,7 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - rejectPurposeVersionErrorMapper + archivePurposeVersionErrorMapper ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 414db45aa8..dca325c2b0 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -71,3 +71,13 @@ export const deletePurposeErrorMapper = (error: ApiError): number => .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("purposeCannotBeDeleted", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const archivePurposeVersionErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 084fa5f4dbd024a37be25f1dd26ab886b1ed2a29 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 11:31:36 +0200 Subject: [PATCH 170/537] Add error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index dca325c2b0..06c19aa357 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -81,3 +81,13 @@ export const archivePurposeVersionErrorMapper = ( .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const suspendedPurposeVersionErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) + .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 14cacb886e359bfdd4c3e77d63629446e7c3a27d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 11:31:43 +0200 Subject: [PATCH 171/537] Add check --- packages/purpose-process/src/services/purposeService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c9c49d4cca..d9c4c8a0d4 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -422,6 +422,10 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); + if (purposeVersion.state !== purposeVersionState.active) { + throw notValidVersionState(purposeVersion.id, purposeVersion.state); + } + const suspendedPurposeVersion: PurposeVersion = { ...purposeVersion, state: purposeVersionState.suspended, From c5ba2794d705815a53d49ca0dd0cea1be2bfa583 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 11:31:48 +0200 Subject: [PATCH 172/537] Update router --- .../src/routers/PurposeRouter.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index c16c4024f2..d2d16cc2d2 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -26,6 +26,7 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, + suspendedPurposeVersionErrorMapper, updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; @@ -251,7 +252,23 @@ const purposeRouter = ( .post( "/purposes/:purposeId/versions/:versionId/suspend", authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + await purposeService.suspendPurposeVersion({ + purposeId: unsafeBrandId(req.params.purposeId), + versionId: unsafeBrandId(req.params.versionId), + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + }); + return res.status(204).end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + suspendedPurposeVersionErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/purposes/:purposeId/versions/:versionId/archive", From 9cb16823bf727ff69182edad12ab3fa6e666fd9c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:10:20 +0200 Subject: [PATCH 173/537] Minor refactor --- packages/purpose-process/src/routers/PurposeRouter.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index ee3bf8e0f0..b52f9eb30a 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -86,7 +86,7 @@ const purposeRouter = ( const { purpose, isRiskAnalysisValid } = await purposeService.getPurposeById( unsafeBrandId(req.params.id), - req.ctx.authData + req.ctx.authData.organizationId ); return res .status(200) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 87086f427a..b06f636af1 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -68,7 +68,7 @@ export function purposeServiceBuilder( return { async getPurposeById( purposeId: PurposeId, - authData: AuthData + organizationId: TenantId ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info(`Retrieving Purpose ${purposeId}`); @@ -77,10 +77,7 @@ export function purposeServiceBuilder( purpose.data.eserviceId, readModelService ); - const tenant = await retrieveTenant( - authData.organizationId, - readModelService - ); + const tenant = await retrieveTenant(organizationId, readModelService); if (tenant.kind === undefined) { throw tenantKindNotFound(tenant.id); From 95cc4b81dfd4bc412891c274203dd0fffa3b1ca4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:13:00 +0200 Subject: [PATCH 174/537] Fix tests --- .../test/purposeService.integration.test.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index b2c17e8bab..4f2ef3c586 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -126,7 +126,7 @@ describe("database test", async () => { const result = await purposeService.getPurposeById( mockPurpose1.id, - getMockAuthData(mockTenant.id) + mockTenant.id ); expect(result).toMatchObject({ purpose: mockPurpose1, @@ -138,7 +138,7 @@ describe("database test", async () => { await addOnePurpose(mockPurpose, postgresDB, purposes); expect( - purposeService.getPurposeById(notExistingId, getMockAuthData()) + purposeService.getPurposeById(notExistingId, generateId()) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); it("should throw eserviceNotFound if the eservice doesn't exist", async () => { @@ -162,10 +162,7 @@ describe("database test", async () => { await writeInReadmodel(mockTenant, tenants); expect( - purposeService.getPurposeById( - mockPurpose1.id, - getMockAuthData(mockTenant.id) - ) + purposeService.getPurposeById(mockPurpose1.id, mockTenant.id) ).rejects.toThrowError(eserviceNotFound(notExistingId)); }); it("should throw tenantNotFound if the tenant doesn't exist", async () => { @@ -186,10 +183,7 @@ describe("database test", async () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.getPurposeById( - mockPurpose1.id, - getMockAuthData(notExistingId) - ) + purposeService.getPurposeById(mockPurpose1.id, notExistingId) ).rejects.toThrowError(tenantNotFound(notExistingId)); }); it("should throw tenantKindNotFound if the tenant doesn't exist", async () => { @@ -211,10 +205,7 @@ describe("database test", async () => { await writeInReadmodel(mockTenant, tenants); expect( - purposeService.getPurposeById( - mockPurpose1.id, - getMockAuthData(mockTenant.id) - ) + purposeService.getPurposeById(mockPurpose1.id, mockTenant.id) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); }); From 560285186cec8ddbfa608ba6d5dc8fda8d33e948 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:13:30 +0200 Subject: [PATCH 175/537] Minor refactor --- packages/purpose-process/src/routers/PurposeRouter.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 402b943938..ef16bc7d5f 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -131,7 +131,7 @@ const purposeRouter = ( purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), documentId: unsafeBrandId(req.params.documentId), - authData: req.ctx.authData, + organizationId: req.ctx.authData.organizationId, }); return res .status(200) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 219bea59ab..b2e6d30f29 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -136,12 +136,12 @@ export function purposeServiceBuilder( purposeId, versionId, documentId, - authData, + organizationId, }: { purposeId: PurposeId; versionId: PurposeVersionId; documentId: PurposeVersionDocumentId; - authData: AuthData; + organizationId: TenantId; }): Promise { const purpose = await retrievePurpose(purposeId, readModelService); const eservice = await retrieveEService( @@ -149,7 +149,7 @@ export function purposeServiceBuilder( readModelService ); getOrganizationRole({ - organizationId: authData.organizationId, + organizationId, producerId: eservice.producerId, consumerId: purpose.data.consumerId, }); From a25f34c517a52255500174924b151cdcca6fe3f8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:14:46 +0200 Subject: [PATCH 176/537] Adjust tests --- .../test/purposeService.integration.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 39eb5c5dbb..ac509dcf26 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -240,7 +240,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, documentId: mockDocument.id, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, }); expect(result).toEqual(mockDocument); }); @@ -266,7 +266,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, documentId: mockDocument.id, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, }) ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); }); @@ -287,7 +287,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, documentId: mockDocument.id, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, }) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); @@ -311,7 +311,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: randomVersionId, documentId: randomDocumentId, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose1.id, randomVersionId) @@ -336,7 +336,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, documentId: randomDocumentId, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, }) ).rejects.toThrowError( purposeVersionDocumentNotFound( @@ -365,7 +365,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, documentId: mockDocument.id, - authData: getMockAuthData(randomId), + organizationId: randomId, }) ).rejects.toThrowError(organizationNotAllowed(randomId)); }); From 2d730aa860e0a8a93dcfad015a4e282bebc813af Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:18:38 +0200 Subject: [PATCH 177/537] Minor refactor --- .../purpose-process/src/routers/PurposeRouter.ts | 2 +- .../src/services/purposeService.ts | 8 ++++---- .../test/purposeService.integration.test.ts | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 1a5ef49041..132d272015 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -126,7 +126,7 @@ const purposeRouter = ( await purposeService.deletePurposeVersion({ purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), - authData: req.ctx.authData, + organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, }); return res.status(204).end(); diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 67bd703fc2..9d79d578e8 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -165,20 +165,20 @@ export function purposeServiceBuilder( async deletePurposeVersion({ purposeId, versionId, - authData, + organizationId, correlationId, }: { purposeId: PurposeId; versionId: PurposeVersionId; - authData: AuthData; + organizationId: TenantId; correlationId: string; }): Promise { logger.info(`Deleting Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); - if (authData.organizationId !== purpose.data.consumerId) { - throw organizationIsNotTheConsumer(authData.organizationId); + if (organizationId !== purpose.data.consumerId) { + throw organizationIsNotTheConsumer(organizationId); } const purposeVersion = retrievePurposeVersion(versionId, purpose); diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index b8dfa21684..2444f1ffaa 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -398,7 +398,7 @@ describe("database test", async () => { await purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }); @@ -447,7 +447,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); @@ -469,7 +469,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: randomVersionId, - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -492,7 +492,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -518,7 +518,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -544,7 +544,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -570,7 +570,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -597,7 +597,7 @@ describe("database test", async () => { purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( From ea2a3c1fffda3ba999256906cb52ca0d49b2e007 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:19:33 +0200 Subject: [PATCH 178/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b06f636af1..87c4d184fe 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,5 +1,4 @@ import { - AuthData, DB, logger, riskAnalysisFormToRiskAnalysisFormToValidate, @@ -86,7 +85,7 @@ export function purposeServiceBuilder( return authorizeRiskAnalysisForm({ purpose: purpose.data, producerId: eservice.producerId, - organizationId: authData.organizationId, + organizationId, tenantKind: tenant.kind, }); }, From 882187c1b82edf8e20e8f45377e4ac038895cecf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:27:29 +0200 Subject: [PATCH 179/537] Fix --- .../src/routers/PurposeRouter.ts | 2 +- .../src/services/purposeService.ts | 16 +++++---------- .../test/purposeService.integration.test.ts | 20 +++++++++---------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index f224036254..98c3c6b14f 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -172,7 +172,7 @@ const purposeRouter = ( await purposeService.rejectPurposeVersion({ purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), - authData: req.ctx.authData, + organizationId: req.ctx.authData.organizationId, rejectionReason: req.body.rejectionReason, correlationId: req.ctx.correlationId, }); diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index f2f34c38e9..3c8e4f36f9 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,10 +1,4 @@ -import { - DB, - eventRepository, - logger, - riskAnalysisFormToRiskAnalysisFormToValidate, - validateRiskAnalysis, -} from "pagopa-interop-commons"; +import { DB, eventRepository, logger } from "pagopa-interop-commons"; import { EService, EServiceId, @@ -210,13 +204,13 @@ export function purposeServiceBuilder( purposeId, versionId, rejectionReason, - authData, + organizationId, correlationId, }: { purposeId: PurposeId; versionId: PurposeVersionId; rejectionReason: string; - authData: AuthData; + organizationId: TenantId; correlationId: string; }): Promise { logger.info(`Rejecting Version ${versionId} in Purpose ${purposeId}`); @@ -226,8 +220,8 @@ export function purposeServiceBuilder( purpose.data.eserviceId, readModelService ); - if (authData.organizationId !== eservice.producerId) { - throw organizationIsNotTheProducer(authData.organizationId); + if (organizationId !== eservice.producerId) { + throw organizationIsNotTheProducer(organizationId); } const purposeVersion = retrievePurposeVersion(versionId, purpose); diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ddf1907c2f..b5f7c74e9f 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -640,7 +640,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }); @@ -698,7 +698,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); @@ -719,7 +719,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); @@ -741,7 +741,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockPurpose1.consumerId), + organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -766,7 +766,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: randomVersionId, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -793,7 +793,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -820,7 +820,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -847,7 +847,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -874,7 +874,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -902,7 +902,7 @@ describe("database test", async () => { purposeId: mockPurpose1.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - authData: getMockAuthData(mockEService.producerId), + organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( From eb0aa222170eedffb9712d217ec8326946a74361 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:35:48 +0200 Subject: [PATCH 180/537] Update events --- packages/models/proto/v2/purpose/events.proto | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/models/proto/v2/purpose/events.proto b/packages/models/proto/v2/purpose/events.proto index d38f3672e3..a75e8541dc 100644 --- a/packages/models/proto/v2/purpose/events.proto +++ b/packages/models/proto/v2/purpose/events.proto @@ -34,7 +34,8 @@ message NewPurposeVersionActivatedV2 { } message PurposeVersionActivatedV2 { - PurposeV2 purpose = 1; + string versionId = 1; + PurposeV2 purpose = 2; } message PurposeVersionUnsuspendedByProducerV2 { @@ -68,7 +69,8 @@ message PurposeVersionOverQuotaUnsuspendedV2 { } message PurposeArchivedV2 { - PurposeV2 purpose = 1; + string versionId = 1; + PurposeV2 purpose = 2; } message WaitingForApprovalPurposeVersionDeletedV2 { From c426f131a113d59983419f7c230985c26ffb527c Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Tue, 16 Apr 2024 14:43:40 +0200 Subject: [PATCH 181/537] TODO --- packages/purpose-process/src/routers/PurposeRouter.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index d2d16cc2d2..305fdbfbfa 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -170,7 +170,9 @@ const purposeRouter = ( .post( "/purposes/:purposeId/versions", authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() + (_req, res) => + // TODO + res.status(501).send() ) .delete( "/purposes/:purposeId/versions/:versionId", From fd47daae56b30d85f1ba14873b5c23e35492e708 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:54:03 +0200 Subject: [PATCH 182/537] Fix output --- packages/purpose-process/src/services/readModelService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index af22c02358..f0f307a85c 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -37,11 +37,11 @@ async function getPurpose( .safeParse(data); if (!result.success) { logger.error( - `Unable to parse eService item: result ${JSON.stringify( + `Unable to parse purpose item: result ${JSON.stringify( result )} - data ${JSON.stringify(data)} ` ); - throw genericError("Unable to parse eService item"); + throw genericError("Unable to parse purpose item"); } return result.data; } @@ -83,11 +83,11 @@ async function getTenant( const result = Tenant.safeParse(data.data); if (!result.success) { logger.error( - `Unable to parse eService item: result ${JSON.stringify( + `Unable to parse tenant item: result ${JSON.stringify( result )} - data ${JSON.stringify(data)} ` ); - throw genericError("Unable to parse eService item"); + throw genericError("Unable to parse tenant item"); } return result.data; } From 9dd006357da9dd77f7de53c3ca4a4a22bf6adcc8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 14:56:44 +0200 Subject: [PATCH 183/537] Simplify tests --- .../test/purposeService.integration.test.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 4f2ef3c586..0da0f90050 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -152,13 +152,7 @@ describe("database test", async () => { ...mockPurpose, eserviceId: notExistingId, }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); expect( @@ -173,13 +167,7 @@ describe("database test", async () => { ...mockPurpose, eserviceId: mockEService.id, }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -194,13 +182,7 @@ describe("database test", async () => { ...mockPurpose, eserviceId: mockEService.id, }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(mockTenant, tenants); From 13b48fedd32256c837871d85c512a6b4bc652949 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 15:03:11 +0200 Subject: [PATCH 184/537] Fix --- .../models/src/purpose/protobufConverterFromV2.ts | 10 +++++----- packages/models/src/purpose/protobufConverterToV2.ts | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index 026802ebda..cf0ad44817 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -7,7 +7,7 @@ import { } from "../gen/v2/purpose/purpose.js"; import { PurposeRiskAnalysisFormV2 } from "../gen/v2/purpose/riskAnalysis.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; -import { bigIntToDate, bigIntToDateOrUndefined } from "../utils.js"; +import { bigIntToDate } from "../utils.js"; import { Purpose, PurposeVersion, @@ -56,9 +56,9 @@ export const fromPurposeVersionV2 = ( ? fromPurposeVersionDocumentV2(input.riskAnalysis) : undefined, createdAt: bigIntToDate(input.createdAt), - updatedAt: bigIntToDateOrUndefined(input.updatedAt), - firstActivationAt: bigIntToDateOrUndefined(input.firstActivationAt), - suspendedAt: bigIntToDateOrUndefined(input.suspendedAt), + updatedAt: bigIntToDate(input.updatedAt), + firstActivationAt: bigIntToDate(input.firstActivationAt), + suspendedAt: bigIntToDate(input.suspendedAt), }); export const fromPurposeRiskAnalysisFormV2 = ( @@ -87,7 +87,7 @@ export const fromPurposeV2 = (input: PurposeV2): Purpose => ({ versions: input.versions.map(fromPurposeVersionV2), isFreeOfCharge: input.isFreeOfCharge, createdAt: bigIntToDate(input.createdAt), - updatedAt: bigIntToDateOrUndefined(input.updatedAt), + updatedAt: bigIntToDate(input.updatedAt), riskAnalysisForm: input.riskAnalysisForm ? fromPurposeRiskAnalysisFormV2(input.riskAnalysisForm) : undefined, diff --git a/packages/models/src/purpose/protobufConverterToV2.ts b/packages/models/src/purpose/protobufConverterToV2.ts index 4556788f5e..735669d8f9 100644 --- a/packages/models/src/purpose/protobufConverterToV2.ts +++ b/packages/models/src/purpose/protobufConverterToV2.ts @@ -5,7 +5,7 @@ import { PurposeVersionDocumentV2, PurposeVersionV2, } from "../gen/v2/purpose/purpose.js"; -import { dateToBigInt, dateToBigIntOrUndefined } from "../utils.js"; +import { dateToBigInt } from "../utils.js"; import { Purpose, PurposeVersion, @@ -41,11 +41,11 @@ export const toPurposeVersionV2 = ( ): PurposeVersionV2 => ({ ...input, state: toPurposeVersionStateV2(input.state), - expectedApprovalDate: dateToBigIntOrUndefined(input.expectedApprovalDate), + expectedApprovalDate: dateToBigInt(input.expectedApprovalDate), createdAt: dateToBigInt(input.createdAt), - updatedAt: dateToBigIntOrUndefined(input.updatedAt), - firstActivationAt: dateToBigIntOrUndefined(input.firstActivationAt), - suspendedAt: dateToBigIntOrUndefined(input.suspendedAt), + updatedAt: dateToBigInt(input.updatedAt), + firstActivationAt: dateToBigInt(input.firstActivationAt), + suspendedAt: dateToBigInt(input.suspendedAt), riskAnalysis: input.riskAnalysis ? toPurposeVersionDocumentV2(input.riskAnalysis) : undefined, @@ -55,5 +55,5 @@ export const toPurposeV2 = (input: Purpose): PurposeV2 => ({ ...input, versions: input.versions.map(toPurposeVersionV2), createdAt: dateToBigInt(input.createdAt), - updatedAt: dateToBigIntOrUndefined(input.updatedAt), + updatedAt: dateToBigInt(input.updatedAt), }); From 00f575f5c90caeadaacceb182769d01a0ce84794 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 15:04:14 +0200 Subject: [PATCH 185/537] Update pnpm lock file --- pnpm-lock.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94b35aa6ac..d5294fa36f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4713,6 +4713,10 @@ packages: resolution: {integrity: sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g==} dev: false + /@types/uuid@9.0.2: + resolution: {integrity: sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==} + dev: true + /@types/uuid@9.0.8: resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} @@ -8644,6 +8648,11 @@ packages: hasBin: true dev: false + /uuid@9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true From d178c3bc887fa9e41e598b83882a0e7e58e1e38a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 15:06:44 +0200 Subject: [PATCH 186/537] Adapt to latest changes --- .../purpose-readmodel-writer/src/index.ts | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/packages/purpose-readmodel-writer/src/index.ts b/packages/purpose-readmodel-writer/src/index.ts index d652cde75b..9aa8bf3e7e 100644 --- a/packages/purpose-readmodel-writer/src/index.ts +++ b/packages/purpose-readmodel-writer/src/index.ts @@ -5,10 +5,9 @@ import { ReadModelRepository, readModelWriterConfig, decodeKafkaMessage, - getContext, purposeTopicConfig, + runWithContext, } from "pagopa-interop-commons"; -import { v4 } from "uuid"; import { runConsumer } from "kafka-iam-auth"; import { PurposeEvent } from "pagopa-interop-models"; import { match } from "ts-pattern"; @@ -24,21 +23,26 @@ async function processMessage({ partition, }: EachMessagePayload): Promise { const decodedMessage = decodeKafkaMessage(message, PurposeEvent); - const ctx = getContext(); - ctx.messageData = { - eventType: decodedMessage.type, - eventVersion: decodedMessage.event_version, - streamId: decodedMessage.stream_id, - }; - ctx.correlationId = decodedMessage.correlation_id || v4(); - await match(decodedMessage) - .with({ event_version: 1 }, (msg) => handleMessageV1(msg, purposes)) - .with({ event_version: 2 }, (msg) => handleMessageV2(msg, purposes)) - .exhaustive(); + runWithContext( + { + messageData: { + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + }, + correlationId: decodedMessage.correlation_id, + }, + async () => { + await match(decodedMessage) + .with({ event_version: 1 }, (msg) => handleMessageV1(msg, purposes)) + .with({ event_version: 2 }, (msg) => handleMessageV2(msg, purposes)) + .exhaustive(); - logger.info( - `Read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + logger.info( + `Read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + ); + } ); } From d7a37362098e06ebbcc4fdf8131b518eef32277b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 16:07:07 +0200 Subject: [PATCH 187/537] Add tests --- .../test/purposeService.integration.test.ts | 546 +++++++++++------- 1 file changed, 333 insertions(+), 213 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index d65aae001b..bd63b47045 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -72,6 +72,7 @@ import { organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, + purposeCannotBeDeleted, purposeNotFound, purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, @@ -931,6 +932,224 @@ describe("database test", async () => { }); }); + describe("updatePurpose and reverseUpdatePurpose", () => { + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + const consumerTenantKind = randomArrayItem(Object.values(tenantKind)); + const consumer: Tenant = { + ...getMockTenant(), + kind: consumerTenantKind, + }; + + const mockEservice: EService = { + ...getMockEService(), + mode: "Deliver", + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEservice.id, + consumerId: consumer.id, + versions: [ + { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }, + ], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(consumer, tenants); + + const mockValidRiskAnalysis = + getMockValidRiskAnalysis(consumerTenantKind); + + const purposeUpdateContent: PurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + riskAnalysisForm: buildRiskAnalysisSeed(mockValidRiskAnalysis), + }; + + await purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: consumer.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + title: purposeUpdateContent.title, + description: purposeUpdateContent.description, + isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + }, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + const producerTenantKind = randomArrayItem(Object.values(tenantKind)); + const producer: Tenant = { + ...getMockTenant(), + kind: producerTenantKind, + }; + + const mockEservice: EService = { + ...getMockEService(), + mode: "Receive", + producerId: producer.id, + }; + + const mockValidRiskAnalysis = + getMockValidRiskAnalysis(producerTenantKind); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEservice.id, + consumerId: producer.id, + versions: [ + { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }, + ], + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: generateId(), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: generateId(), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: generateId(), + }) + ), + }, + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(producer, tenants); + + const reversePurposeUpdateContent: ReversePurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + }; + + await purposeService.updateReversePurpose({ + purposeId: mockPurpose.id, + reversePurposeUpdateContent, + organizationId: producer.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + title: reversePurposeUpdateContent.title, + description: reversePurposeUpdateContent.description, + isFreeOfCharge: reversePurposeUpdateContent.isFreeOfCharge, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + }, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + }); + describe("deletePurpose", () => { it("should write on event-store for the deletion of a purpose (no versions)", () => { expect(1).toBe(1); @@ -941,232 +1160,133 @@ describe("database test", async () => { it("should write on event-store for the deletion of a purpose (waiting for approval version)", () => { expect(1).toBe(1); }); - it("should throw organizationIsNotAConsumer if the requester is not the consumer", () => { - expect(1).toBe(1); - }); - it("should throw purposeCannotBeDeleted if the purpose has an active version ", () => { - expect(1).toBe(1); - }); - it("should throw purposeCannotBeDeleted if the purpose has a rejected version ", () => { - expect(1).toBe(1); - }); - it("should throw purposeCannotBeDeleted if the purpose has a suspeneded version ", () => { - expect(1).toBe(1); - }); - it("should throw purposeCannotBeDeleted if the purpose has an archived version ", () => { - expect(1).toBe(1); - }); - }); - }); - describe("updatePurpose and reverseUpdatePurpose", () => { - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { - const consumerTenantKind = randomArrayItem(Object.values(tenantKind)); - const consumer: Tenant = { - ...getMockTenant(), - kind: consumerTenantKind, - }; - - const mockEservice: EService = { ...getMockEService(), mode: "Deliver" }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEservice.id, - consumerId: consumer.id, - versions: [ - { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }, - ], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(consumer, tenants); - - const mockValidRiskAnalysis = - getMockValidRiskAnalysis(consumerTenantKind); - - const purposeUpdateContent: PurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: false, - riskAnalysisForm: buildRiskAnalysisSeed(mockValidRiskAnalysis), - }; - - await purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: consumer.id, - correlationId: generateId(), + it("should throw purposeNotFound if the purpose doesn't exist", () => { + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(mockPurpose.id)); }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, - }); + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheConsumer(mockEService.producerId) + ); }); + it("should throw purposeCannotBeDeleted if the purpose has an active version ", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - const expectedPurpose: Purpose = { - ...mockPurpose, - title: purposeUpdateContent.title, - description: purposeUpdateContent.description, - isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( - (sa) => sa.key === singleAnswer.key - )!.id - ), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( - (ma) => ma.key === multiAnswer.key - )!.id - ), - }) - ), - }, - }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - }); - it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { - const producerTenantKind = randomArrayItem(Object.values(tenantKind)); - const producer: Tenant = { - ...getMockTenant(), - kind: producerTenantKind, - }; - - const mockEservice: EService = { - ...getMockEService(), - mode: "Receive", - producerId: producer.id, - }; - - const mockValidRiskAnalysis = - getMockValidRiskAnalysis(producerTenantKind); - - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEservice.id, - consumerId: producer.id, - versions: [ - { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }, - ], - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: generateId(), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: generateId(), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: generateId(), - }) - ), - }, - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(producer, tenants); - - const reversePurposeUpdateContent: ReversePurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: false, - }; - - await purposeService.updateReversePurpose({ - purposeId: mockPurpose.id, - reversePurposeUpdateContent, - organizationId: producer.id, - correlationId: generateId(), + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); }); + it("should throw purposeCannotBeDeleted if the purpose has a rejected version ", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); }); + it("should throw purposeCannotBeDeleted if the purpose has a suspeneded version ", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.suspended, + suspendedAt: new Date(), + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); }); + it("should throw purposeCannotBeDeleted if the purpose has an archived version ", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - const expectedPurpose: Purpose = { - ...mockPurpose, - title: reversePurposeUpdateContent.title, - description: reversePurposeUpdateContent.description, - isFreeOfCharge: reversePurposeUpdateContent.isFreeOfCharge, - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( - (sa) => sa.key === singleAnswer.key - )!.id - ), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( - (ma) => ma.key === multiAnswer.key - )!.id - ), - }) - ), - }, - }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); + }); }); }); }); From c86b007806814e411eec8c022448e2f5870a8f08 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 16 Apr 2024 16:11:09 +0200 Subject: [PATCH 188/537] added tests --- .../commons-test/src/riskAnalysisTestUtils.ts | 24 +- .../src/services/purposeService.ts | 2 + .../test/purposeService.integration.test.ts | 444 ++++++++++++------ packages/purpose-process/test/utils.ts | 45 +- 4 files changed, 357 insertions(+), 158 deletions(-) diff --git a/packages/commons-test/src/riskAnalysisTestUtils.ts b/packages/commons-test/src/riskAnalysisTestUtils.ts index 62e44a6bc3..a9f54aeb9f 100644 --- a/packages/commons-test/src/riskAnalysisTestUtils.ts +++ b/packages/commons-test/src/riskAnalysisTestUtils.ts @@ -3,8 +3,14 @@ import { RiskAnalysisFormToValidate, RiskAnalysisValidatedForm, riskAnalysisValidatedFormToNewRiskAnalysis, + riskAnalysisValidatedFormToNewRiskAnalysisForm, } from "pagopa-interop-commons"; -import { RiskAnalysis, TenantKind, tenantKind } from "pagopa-interop-models"; +import { + RiskAnalysis, + RiskAnalysisForm, + TenantKind, + tenantKind, +} from "pagopa-interop-models"; import { match } from "ts-pattern"; import { z } from "zod"; @@ -159,3 +165,19 @@ export const getMockValidRiskAnalysis = ( ) ) .exhaustive(); + +export const getMockValidRiskAnalysisForm = ( + producerTenantKind: TenantKind +): RiskAnalysisForm => + match(producerTenantKind) + .with(tenantKind.PA, () => + riskAnalysisValidatedFormToNewRiskAnalysisForm( + validatedRiskAnalysis3_0_Pa + ) + ) + .with(tenantKind.PRIVATE, tenantKind.GSP, () => + riskAnalysisValidatedFormToNewRiskAnalysisForm( + validatedRiskAnalysis2_0_Private + ) + ) + .exhaustive(); diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 957fb5273d..3f88d1b417 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -283,6 +283,7 @@ export function purposeServiceBuilder( organizationId: TenantId; correlationId: string; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + logger.info(`Updating Purpose ${purposeId}`); return await updatePurposeInternal( purposeId, purposeUpdateContent, @@ -302,6 +303,7 @@ export function purposeServiceBuilder( organizationId: TenantId; correlationId: string; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + logger.info(`Updating Reverse Purpose ${purposeId}`); return await updatePurposeInternal( purposeId, reversePurposeUpdateContent, diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 91501ca938..e1f4b1798b 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -18,6 +18,7 @@ import { ReadModelRepository, TenantCollection, initDB, + unexpectedRulesVersionError, } from "pagopa-interop-commons"; import { IDatabase } from "pg-promise"; import { @@ -30,6 +31,7 @@ import { getMockPurposeVersionDocument, getMockTenant, getMockValidRiskAnalysis, + getMockValidRiskAnalysisForm, mongoDBContainer, postgreSQLContainer, randomArrayItem, @@ -47,6 +49,7 @@ import { PurposeVersionDocumentId, PurposeVersionId, PurposeVersionRejectedV2, + RiskAnalysis, Tenant, TenantId, WaitingForApprovalPurposeVersionDeletedV2, @@ -67,15 +70,19 @@ import { readModelServiceBuilder, } from "../src/services/readModelService.js"; import { + eServiceModeNotAllowed, eserviceNotFound, + missingFreeOfChargeReason, notValidVersionState, organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, purposeNotFound, + purposeNotInDraftState, purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, + riskAnalysisValidationFailed, tenantKindNotFound, tenantNotFound, } from "../src/model/domain/errors.js"; @@ -86,6 +93,7 @@ import { import { addOnePurpose, buildRiskAnalysisSeed, + createUpdatedPurpose, getMockEService, } from "./utils.js"; @@ -128,7 +136,8 @@ describe("database test", async () => { afterEach(async () => { await purposes.deleteMany({}); - + await tenants.deleteMany({}); + await eservices.deleteMany({}); await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); }); @@ -932,56 +941,84 @@ describe("database test", async () => { }); }); describe("updatePurpose and reverseUpdatePurpose", () => { - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { - const consumerTenantKind = randomArrayItem(Object.values(tenantKind)); - const consumer: Tenant = { - ...getMockTenant(), - kind: consumerTenantKind, - }; - - const mockEservice: EService = { ...getMockEService(), mode: "Deliver" }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEservice.id, - consumerId: consumer.id, - versions: [ - { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }, - ], - }; + const tenantType = randomArrayItem(Object.values(tenantKind)); + const tenant: Tenant = { + ...getMockTenant(), + kind: tenantType, + }; + + const eServiceDeliver: EService = { + ...getMockEService(), + mode: "Deliver", + }; + + const eServiceReceive: EService = { + ...getMockEService(), + mode: "Receive", + producerId: tenant.id, + }; + + const purposeForReceive: Purpose = { + ...getMockPurpose(), + eserviceId: eServiceReceive.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + riskAnalysisForm: { + ...getMockValidRiskAnalysisForm(tenantType), + id: generateId(), + }, + }; + + const purposeForDeliver: Purpose = { + ...getMockPurpose(), + eserviceId: eServiceDeliver.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + }; + + const validRiskAnalysis = getMockValidRiskAnalysis(tenantType); + + const purposeUpdateContent: PurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: true, + freeOfChargeReason: "reason", + riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), + }; + + const reversePurposeUpdateContent: ReversePurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: true, + freeOfChargeReason: "reason", + }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(consumer, tenants); - - const mockValidRiskAnalysis = - getMockValidRiskAnalysis(consumerTenantKind); - - const purposeUpdateContent: PurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: false, - riskAnalysisForm: buildRiskAnalysisSeed(mockValidRiskAnalysis), - }; + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); await purposeService.updatePurpose({ - purposeId: mockPurpose.id, + purposeId: purposeForDeliver.id, purposeUpdateContent, - organizationId: consumer.id, + organizationId: tenant.id, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, + purposeForDeliver.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, + stream_id: purposeForDeliver.id, version: "1", type: "DraftPurposeUpdated", event_version: 2, @@ -992,111 +1029,35 @@ describe("database test", async () => { payload: writtenEvent.data, }); - const expectedPurpose: Purpose = { - ...mockPurpose, - title: purposeUpdateContent.title, - description: purposeUpdateContent.description, - isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( - (sa) => sa.key === singleAnswer.key - )!.id - ), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( - (ma) => ma.key === multiAnswer.key - )!.id - ), - }) - ), - }, - }; + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForDeliver, + purposeUpdateContent, + validRiskAnalysis, + writtenPayload + ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { - const producerTenantKind = randomArrayItem(Object.values(tenantKind)); - const producer: Tenant = { - ...getMockTenant(), - kind: producerTenantKind, - }; - - const mockEservice: EService = { - ...getMockEService(), - mode: "Receive", - producerId: producer.id, - }; - - const mockValidRiskAnalysis = - getMockValidRiskAnalysis(producerTenantKind); - - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEservice.id, - consumerId: producer.id, - versions: [ - { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }, - ], - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: generateId(), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: generateId(), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: generateId(), - }) - ), - }, - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(producer, tenants); - - const reversePurposeUpdateContent: ReversePurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: false, - }; + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); await purposeService.updateReversePurpose({ - purposeId: mockPurpose.id, + purposeId: purposeForReceive.id, reversePurposeUpdateContent, - organizationId: producer.id, + organizationId: tenant.id, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, + purposeForReceive.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, + stream_id: purposeForReceive.id, version: "1", type: "DraftPurposeUpdated", event_version: 2, @@ -1107,39 +1068,210 @@ describe("database test", async () => { payload: writtenEvent.data, }); - const expectedPurpose: Purpose = { - ...mockPurpose, - title: reversePurposeUpdateContent.title, - description: reversePurposeUpdateContent.description, - isFreeOfCharge: reversePurposeUpdateContent.isFreeOfCharge, + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForReceive, + reversePurposeUpdateContent, + validRiskAnalysis, + writtenPayload + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const purposeId: PurposeId = unsafeBrandId(generateId()); + + expect( + purposeService.updatePurpose({ + purposeId, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(purposeId)); + }); + it("Should throw organizationIsNotTheConsumer if the organization is not the consumer", async () => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + consumerId: generateId(), + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const organizationId: TenantId = unsafeBrandId(generateId()); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId, + correlationId: generateId(), + }) + ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); + }); + it("Should throw purposeNotInDraftState if the purpose is not in draft state", async () => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.active }, + ], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); + }); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const eserviceId: EServiceId = unsafeBrandId(generateId()); + const mockPurpose: Purpose = { + ...purposeForDeliver, + eserviceId, + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(eserviceNotFound(eserviceId)); + }); + it("should throw eServiceModeNotAllowed if the eService mode is incorrect", async () => { + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForReceive.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + eServiceModeNotAllowed(eServiceReceive.id, "Deliver") + ); + + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeForDeliver.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + eServiceModeNotAllowed(eServiceDeliver.id, "Receive") + ); + }); + it("Should throw missingFreeOfChargeReason if the freeOfChargeReason is missing", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: { + ...purposeUpdateContent, + freeOfChargeReason: undefined, + }, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(missingFreeOfChargeReason()); + }); + it("Should throw tenantNotFound if the tenant does not exist", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeForReceive.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + }); + it("Should throw tenantKindNotFound if the tenant kind does not exist", async () => { + const mockTenant = { + ...tenant, + kind: undefined, + }; + + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: mockTenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const invalidRiskAnalysis: RiskAnalysis = { + ...validRiskAnalysis, riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( - (sa) => sa.key === singleAnswer.key - )!.id - ), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( - (ma) => ma.key === multiAnswer.key - )!.id - ), - }) - ), + ...validRiskAnalysis.riskAnalysisForm, + version: "0", }, }; - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + const mockPurposeUpdateContent: PurposeUpdateContent = { + ...purposeUpdateContent, + riskAnalysisForm: buildRiskAnalysisSeed(invalidRiskAnalysis), + }; + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: mockPurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) + ); }); }); }); diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 88ea2169e2..56732ebaee 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -7,6 +7,7 @@ import { writeInReadmodel, } from "pagopa-interop-commons-test"; import { + DraftPurposeUpdatedV2, EService, Purpose, PurposeEvent, @@ -15,9 +16,14 @@ import { purposeEventToBinaryData, technology, toPurposeV2, + unsafeBrandId, } from "pagopa-interop-models"; import { IDatabase } from "pg-promise"; -import { RiskAnalysisFormSeed } from "../src/model/domain/models.js"; +import { + PurposeUpdateContent, + ReversePurposeUpdateContent, + RiskAnalysisFormSeed, +} from "../src/model/domain/models.js"; export const addOnePurpose = async ( purpose: Purpose, @@ -66,3 +72,40 @@ export const buildRiskAnalysisSeed = ( riskAnalysis: RiskAnalysis ): RiskAnalysisFormSeed => riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysis.riskAnalysisForm); + +export const createUpdatedPurpose = ( + mockPurpose: Purpose, + purposeUpdateContent: PurposeUpdateContent | ReversePurposeUpdateContent, + mockValidRiskAnalysis: RiskAnalysis, + writtenPayload: DraftPurposeUpdatedV2 +): Purpose => ({ + ...mockPurpose, + title: purposeUpdateContent.title, + description: purposeUpdateContent.description, + isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, + freeOfChargeReason: purposeUpdateContent.freeOfChargeReason, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + }, +}); From 1073c772126607201500b53de95022ab571f15cc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 16:17:49 +0200 Subject: [PATCH 189/537] Add tests --- .../test/purposeService.integration.test.ts | 124 +++++++++++++++++- 1 file changed, 118 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index bd63b47045..9e8cec8aaa 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -38,6 +38,7 @@ import { } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; import { + DraftPurposeDeletedV2, DraftPurposeUpdatedV2, EService, EServiceId, @@ -49,6 +50,7 @@ import { PurposeVersionRejectedV2, Tenant, TenantId, + WaitingForApprovalPurposeDeletedV2, WaitingForApprovalPurposeVersionDeletedV2, generateId, purposeVersionState, @@ -1151,14 +1153,124 @@ describe("database test", async () => { }); describe("deletePurpose", () => { - it("should write on event-store for the deletion of a purpose (no versions)", () => { - expect(1).toBe(1); + it("should write on event-store for the deletion of a purpose (no versions)", async () => { + const mockEService = getMockEService(); + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "DraftPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); }); - it("should write on event-store for the deletion of a purpose (draft version)", () => { - expect(1).toBe(1); + it("should write on event-store for the deletion of a purpose (draft version)", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "DraftPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); }); - it("should write on event-store for the deletion of a purpose (waiting for approval version)", () => { - expect(1).toBe(1); + it("should write on event-store for the deletion of a purpose (waiting for approval version)", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurpose({ + purposeId: mockPurpose1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "WaitingForApprovalPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: WaitingForApprovalPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); }); it("should throw purposeNotFound if the purpose doesn't exist", () => { expect( From ded455f726c7a6fde26df0cf54f06ace0fd68f3b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 16:46:28 +0200 Subject: [PATCH 190/537] Add tests --- .../test/purposeService.integration.test.ts | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 9e8cec8aaa..249454b3b8 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1400,5 +1400,137 @@ describe("database test", async () => { ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); }); }); + + describe("archivePurposeVersion", () => { + it("should write on event-store for the archiving of a purpose", () => {}); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomPurposeId: PurposeId = generateId(); + const randomVersionId: PurposeVersionId = generateId(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: randomPurposeId, + versionId: randomVersionId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomPurposeId)); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const randomOrganizationId: TenantId = generateId(); + + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: randomOrganizationId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheConsumer(randomOrganizationId) + ); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: randomVersionId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose1.id, randomVersionId) + ); + }); + it("should throw notValidVersionState if the purpose version is in draft state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in waitingForApproval state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in rejected state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + }); }); }); From 122947c2791da9d30250951b65ac457fb383b575 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 16:53:47 +0200 Subject: [PATCH 191/537] Add tests --- .../test/purposeService.integration.test.ts | 118 +++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 249454b3b8..1af1c38bd8 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -43,6 +43,7 @@ import { EService, EServiceId, Purpose, + PurposeArchivedV2, PurposeId, PurposeVersion, PurposeVersionDocumentId, @@ -1402,7 +1403,122 @@ describe("database test", async () => { }); describe("archivePurposeVersion", () => { - it("should write on event-store for the archiving of a purpose", () => {}); + it("should write on event-store for the archiving of a purpose version", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "PurposeArchived", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose1, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.rejected, + updatedAt: new Date(), + }, + ], + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeArchivedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should write on event-store for the archiving of a purpose version, and delete waitingForApprovalVersions", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurposeVersion2: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + versions: [mockPurposeVersion1, mockPurposeVersion2], + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "PurposeArchived", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose1, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.rejected, + updatedAt: new Date(), + }, + ], + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeArchivedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const randomPurposeId: PurposeId = generateId(); const randomVersionId: PurposeVersionId = generateId(); From ff0a0def58003ae012ca96062d52cae75a46531d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 17:22:06 +0200 Subject: [PATCH 192/537] Update router --- packages/purpose-process/src/routers/PurposeRouter.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index d2d16cc2d2..61ced77e13 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -254,13 +254,16 @@ const purposeRouter = ( authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { try { - await purposeService.suspendPurposeVersion({ + const suspendedVersion = await purposeService.suspendPurposeVersion({ purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, }); - return res.status(204).end(); + return res + .status(200) + .json(purposeVersionToApiPurposeVersion(suspendedVersion)) + .end(); } catch (error) { const errorRes = makeApiProblem( error, From 20204732f2b102c2c71c3a48517cf85633d77726 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 16 Apr 2024 17:49:30 +0200 Subject: [PATCH 193/537] missing import --- packages/purpose-process/src/services/purposeService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ea483db99b..668f5fa28a 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,4 +1,9 @@ -import { CreateEvent, DB, eventRepository, logger } from "pagopa-interop-commons"; +import { + CreateEvent, + DB, + eventRepository, + logger, +} from "pagopa-interop-commons"; import { EService, EServiceId, From 52289636af7ce54cb8a7e06623b0817b14b1e438 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:01:49 +0200 Subject: [PATCH 194/537] Fix logic --- packages/purpose-process/src/services/purposeService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d9c4c8a0d4..8541252902 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -422,7 +422,10 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); - if (purposeVersion.state !== purposeVersionState.active) { + if ( + purposeVersion.state !== purposeVersionState.active && + purposeVersion.state !== purposeVersionState.suspended + ) { throw notValidVersionState(purposeVersion.id, purposeVersion.state); } From 413d56c35e32920bfee5ae31d2d2418f88911e81 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:01:53 +0200 Subject: [PATCH 195/537] Add tests --- .../test/purposeService.integration.test.ts | 245 +++++++++++++++++- 1 file changed, 242 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 1af1c38bd8..b239f13191 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -49,6 +49,8 @@ import { PurposeVersionDocumentId, PurposeVersionId, PurposeVersionRejectedV2, + PurposeVersionSuspendedByConsumerV2, + PurposeVersionSuspendedByProducerV2, Tenant, TenantId, WaitingForApprovalPurposeDeletedV2, @@ -93,7 +95,7 @@ import { getMockEService, } from "./utils.js"; -describe("database test", async () => { +describe("Integration tests", async () => { let purposes: PurposeCollection; let eservices: EServiceCollection; let tenants: TenantCollection; @@ -1463,7 +1465,6 @@ describe("database test", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); - const mockEService = getMockEService(); const mockPurposeVersion1: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.active, @@ -1477,7 +1478,6 @@ describe("database test", async () => { versions: [mockPurposeVersion1, mockPurposeVersion2], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.archivePurposeVersion({ purposeId: mockPurpose1.id, @@ -1648,5 +1648,244 @@ describe("database test", async () => { ); }); }); + + describe("suspendPurposeVersion", () => { + it("should write on event-store for the suspension of a purpose version by the consumer", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion1], + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "PurposeVersionSuspendedByConsumer", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose1, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.suspended, + suspendedAt: new Date(), + }, + ], + suspendedByConsumer: true, + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionSuspendedByConsumerV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should write on event-store for the suspension of a purpose version by the producer", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion1], + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion1.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose1.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose1.id, + version: "1", + type: "PurposeVersionSuspendedByProducer", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose1, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.suspended, + suspendedAt: new Date(), + }, + ], + suspendedByProducer: true, + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionSuspendedByProducerV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomPurposeId: PurposeId = generateId(); + const randomVersionId: PurposeVersionId = generateId(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: randomPurposeId, + versionId: randomVersionId, + organizationId: generateId(), + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomPurposeId)); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", () => {}); + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", () => {}); + it("should throw notValidVersionState if the purpose version is in draft state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in waitingForApproval state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in rejected state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in draft", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + }); }); }); From 089c7e3dbcec5919437caf512a57d3cec74f5118 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:03:33 +0200 Subject: [PATCH 196/537] Simplify tests --- .../purpose-process/test/purposeService.integration.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 1af1c38bd8..3de1605269 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1407,7 +1407,6 @@ describe("database test", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); - const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.active, @@ -1417,7 +1416,6 @@ describe("database test", async () => { versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.archivePurposeVersion({ purposeId: mockPurpose1.id, @@ -1463,7 +1461,6 @@ describe("database test", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); - const mockEService = getMockEService(); const mockPurposeVersion1: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.active, @@ -1477,7 +1474,6 @@ describe("database test", async () => { versions: [mockPurposeVersion1, mockPurposeVersion2], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.archivePurposeVersion({ purposeId: mockPurpose1.id, From b83b2fa7b699d436f9ce005c9e67ce24bc336346 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:04:50 +0200 Subject: [PATCH 197/537] Refactor --- .../purpose-process/test/purposeService.integration.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 0da0f90050..a2e922ee76 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -102,9 +102,9 @@ describe("database test", async () => { describe("Purpose service", () => { const mockPurpose = getMockPurpose(); + const mockEService = getMockEService(); describe("getPurposeById", () => { it("should get the purpose if it exists", async () => { - const mockEService = getMockEService(); const mockTenant = { ...getMockTenant(), kind: tenantKind.PA, @@ -160,7 +160,6 @@ describe("database test", async () => { ).rejects.toThrowError(eserviceNotFound(notExistingId)); }); it("should throw tenantNotFound if the tenant doesn't exist", async () => { - const mockEService = getMockEService(); const notExistingId: TenantId = generateId(); const mockPurpose1: Purpose = { @@ -175,7 +174,6 @@ describe("database test", async () => { ).rejects.toThrowError(tenantNotFound(notExistingId)); }); it("should throw tenantKindNotFound if the tenant doesn't exist", async () => { - const mockEService = getMockEService(); const mockTenant = getMockTenant(); const mockPurpose1: Purpose = { From 0952e4bf0c0e9c82aaaae6d96e536e30cb010a72 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:08:56 +0200 Subject: [PATCH 198/537] Refactor --- .../test/purposeService.integration.test.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 96bdcc0daa..4113d314e4 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -98,6 +98,7 @@ describe("database test", async () => { afterEach(async () => { await purposes.deleteMany({}); + await eservices.deleteMany({}); await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); }); @@ -110,6 +111,7 @@ describe("database test", async () => { describe("Purpose service", () => { const mockPurpose = getMockPurpose(); const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); describe("getPurposeById", () => { it("should get the purpose if it exists", async () => { const mockTenant = { @@ -199,8 +201,6 @@ describe("database test", async () => { describe("getRiskAnalysisDocument", () => { it("should get the purpose version document", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); const mockPurpose1: Purpose = { ...mockPurpose, @@ -225,8 +225,6 @@ describe("database test", async () => { expect(result).toEqual(mockDocument); }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); const mockPurpose1: Purpose = { ...mockPurpose, @@ -251,8 +249,6 @@ describe("database test", async () => { ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); }); it("should throw eserviceNotFound if the eservice doesn't exist", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); const mockPurpose1: Purpose = { ...mockPurpose, @@ -272,8 +268,6 @@ describe("database test", async () => { ).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const randomVersionId: PurposeVersionId = generateId(); const randomDocumentId: PurposeVersionDocumentId = generateId(); const mockDocument = getMockPurposeVersionDocument(); @@ -298,8 +292,6 @@ describe("database test", async () => { ); }); it("should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); const randomDocumentId: PurposeVersionDocumentId = generateId(); const mockPurpose1: Purpose = { @@ -328,8 +320,6 @@ describe("database test", async () => { }); it("should throw organizationNotAllowed if the requester is not the producer not the consumer", async () => { const randomId: TenantId = generateId(); - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockDocument = getMockPurposeVersionDocument(); const mockPurpose1: Purpose = { ...mockPurpose, From a7f0110098d45ff4e09b7c8e4fa4afcefa907930 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:09:26 +0200 Subject: [PATCH 199/537] Fix --- packages/purpose-process/test/purposeService.integration.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a2e922ee76..c3949c69d5 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -91,6 +91,7 @@ describe("database test", async () => { afterEach(async () => { await purposes.deleteMany({}); + await eservices.deleteMany({}); await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); }); From 5fc7c8244157b690547a3c33e9a5c6f050a181a0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 16 Apr 2024 18:14:41 +0200 Subject: [PATCH 200/537] Refactor --- .../test/purposeService.integration.test.ts | 53 ++++++++----------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ebac4fdc34..d9e184eea4 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -117,6 +117,8 @@ describe("database test", async () => { describe("Purpose service", () => { const mockPurpose = getMockPurpose(); + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); describe("getPurposeById", () => { it("should get the purpose if it exists", async () => { const mockEService = getMockEService(); @@ -363,7 +365,6 @@ describe("database test", async () => { describe("deletePurposeVersion", () => { it("should write in event-store for the deletion of a purpose version", async () => { - const mockEService = getMockEService(); const mockPurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, @@ -410,8 +411,6 @@ describe("database test", async () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, @@ -435,8 +434,6 @@ describe("database test", async () => { ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const randomVersionId: PurposeVersionId = generateId(); const mockPurpose1: Purpose = { ...mockPurpose, @@ -459,8 +456,6 @@ describe("database test", async () => { ); }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, @@ -482,15 +477,14 @@ describe("database test", async () => { ); }); it("should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), + const mockPurposeVersion1: PurposeVersion = { + ...mockPurposeVersion, state: purposeVersionState.draft, }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [mockPurposeVersion1], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -499,24 +493,23 @@ describe("database test", async () => { expect( purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, - versionId: mockPurposeVersion.id, + versionId: mockPurposeVersion1.id, organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion1.id) ); }); it("should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), + const mockPurposeVersion1: PurposeVersion = { + ...mockPurposeVersion, state: purposeVersionState.active, }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [mockPurposeVersion1], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -525,24 +518,23 @@ describe("database test", async () => { expect( purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, - versionId: mockPurposeVersion.id, + versionId: mockPurposeVersion1.id, organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion1.id) ); }); it("should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), + const mockPurposeVersion1: PurposeVersion = { + ...mockPurposeVersion, state: purposeVersionState.archived, }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [mockPurposeVersion1], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -551,25 +543,24 @@ describe("database test", async () => { expect( purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, - versionId: mockPurposeVersion.id, + versionId: mockPurposeVersion1.id, organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion1.id) ); }); it("should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), + const mockPurposeVersion1: PurposeVersion = { + ...mockPurposeVersion, state: purposeVersionState.suspended, suspendedAt: new Date(), }; const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [mockPurposeVersion1], }; await addOnePurpose(mockPurpose1, postgresDB, purposes); @@ -578,12 +569,12 @@ describe("database test", async () => { expect( purposeService.deletePurposeVersion({ purposeId: mockPurpose1.id, - versionId: mockPurposeVersion.id, + versionId: mockPurposeVersion1.id, organizationId: mockPurpose1.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion.id) + purposeVersionCannotBeDeleted(mockPurpose1.id, mockPurposeVersion1.id) ); }); }); From 62376a86dcaf12a9fc9047717e07623441d4b5ef Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 17 Apr 2024 09:08:19 +0200 Subject: [PATCH 201/537] fix as suggested in review --- .../src/model/domain/models.ts | 6 +- .../src/services/purposeService.ts | 20 +- .../src/services/validators.ts | 10 +- .../test/purposeService.integration.test.ts | 593 +++++++++--------- 4 files changed, 313 insertions(+), 316 deletions(-) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 83c06470f9..0b29388e96 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -11,14 +11,14 @@ export type ApiPurposeVersionDocument = z.infer< typeof api.schemas.PurposeVersionDocument >; -export type PurposeUpdateContent = z.infer< +export type ApiPurposeUpdateContent = z.infer< typeof api.schemas.PurposeUpdateContent >; -export type RiskAnalysisFormSeed = z.infer< +export type ApiRiskAnalysisFormSeed = z.infer< typeof api.schemas.RiskAnalysisFormSeed >; -export type ReversePurposeUpdateContent = z.infer< +export type ApiReversePurposeUpdateContent = z.infer< typeof api.schemas.ReversePurposeUpdateContent >; diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 668f5fa28a..fe41bcb270 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -43,14 +43,14 @@ import { toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { - PurposeUpdateContent, - ReversePurposeUpdateContent, + ApiPurposeUpdateContent, + ApiReversePurposeUpdateContent, } from "../model/domain/models.js"; import { ReadModelService } from "./readModelService.js"; import { assertOrganizationIsAConsumer, - isEserviceMode, - isFreeOfCharge, + assertEserviceHasSpecificMode, + assertConsistentFreeOfCharge, isRiskAnalysisFormValid, purposeIsDraft, assertTenantKindExists, @@ -276,7 +276,7 @@ export function purposeServiceBuilder( correlationId, }: { purposeId: PurposeId; - purposeUpdateContent: PurposeUpdateContent; + purposeUpdateContent: ApiPurposeUpdateContent; organizationId: TenantId; correlationId: string; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { @@ -296,7 +296,7 @@ export function purposeServiceBuilder( correlationId, }: { purposeId: PurposeId; - reversePurposeUpdateContent: ReversePurposeUpdateContent; + reversePurposeUpdateContent: ApiReversePurposeUpdateContent; organizationId: TenantId; correlationId: string; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { @@ -392,7 +392,7 @@ const getInvolvedTenantByEServiceMode = async ( const updatePurposeInternal = async ( purposeId: PurposeId, - updateContent: PurposeUpdateContent | ReversePurposeUpdateContent, + updateContent: ApiPurposeUpdateContent | ApiReversePurposeUpdateContent, organizationId: TenantId, eserviceMode: EServiceMode, { @@ -415,8 +415,8 @@ const updatePurposeInternal = async ( purpose.data.eserviceId, readModelService ); - isEserviceMode(eservice, eserviceMode); - isFreeOfCharge( + assertEserviceHasSpecificMode(eservice, eserviceMode); + assertConsistentFreeOfCharge( updateContent.isFreeOfCharge, updateContent.freeOfChargeReason ); @@ -432,7 +432,7 @@ const updatePurposeInternal = async ( const newRiskAnalysis: PurposeRiskAnalysisForm | undefined = eserviceMode === "Deliver" ? validateAndTransformRiskAnalysis( - (updateContent as PurposeUpdateContent).riskAnalysisForm, + (updateContent as ApiPurposeUpdateContent).riskAnalysisForm, tenant.kind ) : reverseValidateAndTransformRiskAnalysis( diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 8e056cf789..16c171514f 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -23,7 +23,7 @@ import { riskAnalysisValidationFailed, tenantKindNotFound, } from "../model/domain/errors.js"; -import { RiskAnalysisFormSeed } from "../model/domain/models.js"; +import { ApiRiskAnalysisFormSeed } from "../model/domain/models.js"; export const isRiskAnalysisFormValid = ( riskAnalysisForm: RiskAnalysisForm | undefined, @@ -46,7 +46,7 @@ export const isRiskAnalysisFormValid = ( export const purposeIsDraft = (purpose: Purpose): boolean => !purpose.versions.some((v) => v.state !== purposeVersionState.draft); -export const isEserviceMode = ( +export const assertEserviceHasSpecificMode = ( eservice: EService, expectedMode: EServiceMode ): void => { @@ -55,7 +55,7 @@ export const isEserviceMode = ( } }; -export const isFreeOfCharge = ( +export const assertConsistentFreeOfCharge = ( isFreeOfCharge: boolean, freeOfChargeReason: string | undefined ): void => { @@ -74,7 +74,7 @@ export const assertOrganizationIsAConsumer = ( }; export function validateRiskAnalysisSchemaOrThrow( - riskAnalysisForm: RiskAnalysisFormSeed, + riskAnalysisForm: ApiRiskAnalysisFormSeed, tenantKind: TenantKind ): RiskAnalysisValidatedForm { const result = validateRiskAnalysis(riskAnalysisForm, true, tenantKind); @@ -86,7 +86,7 @@ export function validateRiskAnalysisSchemaOrThrow( } export function validateAndTransformRiskAnalysis( - riskAnalysisForm: RiskAnalysisFormSeed | undefined, + riskAnalysisForm: ApiRiskAnalysisFormSeed | undefined, tenantKind: TenantKind ): PurposeRiskAnalysisForm | undefined { if (!riskAnalysisForm) { diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 898087cc5b..79f713284e 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -912,339 +912,336 @@ describe("database test", async () => { ); }); }); - }); - describe("updatePurpose and reverseUpdatePurpose", () => { - const tenantType = randomArrayItem(Object.values(tenantKind)); - const tenant: Tenant = { - ...getMockTenant(), - kind: tenantType, - }; - - const eServiceDeliver: EService = { - ...getMockEService(), - mode: "Deliver", - }; - - const eServiceReceive: EService = { - ...getMockEService(), - mode: "Receive", - producerId: tenant.id, - }; - - const purposeForReceive: Purpose = { - ...getMockPurpose(), - eserviceId: eServiceReceive.id, - consumerId: tenant.id, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.draft }, - ], - riskAnalysisForm: { - ...getMockValidRiskAnalysisForm(tenantType), - id: generateId(), - }, - }; - - const purposeForDeliver: Purpose = { - ...getMockPurpose(), - eserviceId: eServiceDeliver.id, - consumerId: tenant.id, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.draft }, - ], - }; - - const validRiskAnalysis = getMockValidRiskAnalysis(tenantType); - - const purposeUpdateContent: PurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: true, - freeOfChargeReason: "reason", - riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), - }; - - const reversePurposeUpdateContent: ReversePurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: true, - freeOfChargeReason: "reason", - }; - - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - await purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }); - const writtenEvent = await readLastEventByStreamId( - purposeForDeliver.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: purposeForDeliver.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, - }); + describe("updatePurpose and reverseUpdatePurpose", () => { + const tenantType = randomArrayItem(Object.values(tenantKind)); + const tenant: Tenant = { + ...getMockTenant(), + kind: tenantType, + }; - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, - }); + const eServiceDeliver: EService = { + ...getMockEService(), + mode: "Deliver", + }; - const expectedPurpose: Purpose = createUpdatedPurpose( - purposeForDeliver, - purposeUpdateContent, - validRiskAnalysis, - writtenPayload - ); + const eServiceReceive: EService = { + ...getMockEService(), + mode: "Receive", + producerId: tenant.id, + }; - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - }); - it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { - await addOnePurpose(purposeForReceive, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); - await writeInReadmodel(tenant, tenants); - - await purposeService.updateReversePurpose({ - purposeId: purposeForReceive.id, - reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }); + const purposeForReceive: Purpose = { + ...getMockPurpose(), + eserviceId: eServiceReceive.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + riskAnalysisForm: { + ...getMockValidRiskAnalysisForm(tenantType), + id: generateId(), + }, + }; - const writtenEvent = await readLastEventByStreamId( - purposeForReceive.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: purposeForReceive.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, - }); + const purposeForDeliver: Purpose = { + ...getMockPurpose(), + eserviceId: eServiceDeliver.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + }; - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, - }); + const validRiskAnalysis = getMockValidRiskAnalysis(tenantType); - const expectedPurpose: Purpose = createUpdatedPurpose( - purposeForReceive, - reversePurposeUpdateContent, - validRiskAnalysis, - writtenPayload - ); + const purposeUpdateContent: PurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: true, + freeOfChargeReason: "reason", + riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), + }; - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - }); - it("Should throw purposeNotFound if the purpose doesn't exist", async () => { - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); + const reversePurposeUpdateContent: ReversePurposeUpdateContent = { + ...purposeUpdateContent, + }; - const purposeId: PurposeId = unsafeBrandId(generateId()); + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); - expect( - purposeService.updatePurpose({ - purposeId, + await purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), - }) - ).rejects.toThrowError(purposeNotFound(purposeId)); - }); - it("Should throw organizationIsNotTheConsumer if the organization is not the consumer", async () => { - const mockPurpose: Purpose = { - ...purposeForDeliver, - consumerId: generateId(), - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); + }); - const organizationId: TenantId = unsafeBrandId(generateId()); + const writtenEvent = await readLastEventByStreamId( + purposeForDeliver.id, + "purpose", + postgresDB + ); - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId, - correlationId: generateId(), - }) - ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); - }); - it("Should throw purposeNotInDraftState if the purpose is not in draft state", async () => { - const mockPurpose: Purpose = { - ...purposeForDeliver, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.active }, - ], - }; + expect(writtenEvent).toMatchObject({ + stream_id: purposeForDeliver.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForDeliver, purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); - }); - it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { - const eserviceId: EServiceId = unsafeBrandId(generateId()); - const mockPurpose: Purpose = { - ...purposeForDeliver, - eserviceId, - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(tenant, tenants); + validRiskAnalysis, + writtenPayload + ); - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(eserviceNotFound(eserviceId)); - }); - it("should throw eServiceModeNotAllowed if the eService mode is incorrect", async () => { - await addOnePurpose(purposeForReceive, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); - await writeInReadmodel(tenant, tenants); + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); - expect( - purposeService.updatePurpose({ + await purposeService.updateReversePurpose({ purposeId: purposeForReceive.id, - purposeUpdateContent, + reversePurposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), - }) - ).rejects.toThrowError( - eServiceModeNotAllowed(eServiceReceive.id, "Deliver") - ); + }); - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + const writtenEvent = await readLastEventByStreamId( + purposeForReceive.id, + "purpose", + postgresDB + ); - expect( - purposeService.updateReversePurpose({ - purposeId: purposeForDeliver.id, + expect(writtenEvent).toMatchObject({ + stream_id: purposeForReceive.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForReceive, reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError( - eServiceModeNotAllowed(eServiceDeliver.id, "Receive") - ); - }); - it("Should throw missingFreeOfChargeReason if the freeOfChargeReason is missing", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); + validRiskAnalysis, + writtenPayload + ); - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: { - ...purposeUpdateContent, - freeOfChargeReason: undefined, - }, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(missingFreeOfChargeReason()); - }); - it("Should throw tenantNotFound if the tenant does not exist", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(tenantNotFound(tenant.id)); + const purposeId: PurposeId = unsafeBrandId(generateId()); + + expect( + purposeService.updatePurpose({ + purposeId, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(purposeId)); + }); + it("Should throw organizationIsNotTheConsumer if the organization is not the consumer", async () => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + consumerId: generateId(), + }; - await addOnePurpose(purposeForReceive, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); - expect( - purposeService.updateReversePurpose({ - purposeId: purposeForReceive.id, - reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(tenantNotFound(tenant.id)); - }); - it("Should throw tenantKindNotFound if the tenant kind does not exist", async () => { - const mockTenant = { - ...tenant, - kind: undefined, - }; + const organizationId: TenantId = unsafeBrandId(generateId()); - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(mockTenant, tenants); + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId, + correlationId: generateId(), + }) + ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); + }); + it("Should throw purposeNotInDraftState if the purpose is not in draft state", async () => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.active }, + ], + }; - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: mockTenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); - }); - it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); - const invalidRiskAnalysis: RiskAnalysis = { - ...validRiskAnalysis, - riskAnalysisForm: { - ...validRiskAnalysis.riskAnalysisForm, - version: "0", - }, - }; + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); + }); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const eserviceId: EServiceId = unsafeBrandId(generateId()); + const mockPurpose: Purpose = { + ...purposeForDeliver, + eserviceId, + }; - const mockPurposeUpdateContent: PurposeUpdateContent = { - ...purposeUpdateContent, - riskAnalysisForm: buildRiskAnalysisSeed(invalidRiskAnalysis), - }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(tenant, tenants); - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: mockPurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError( - riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) - ); + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(eserviceNotFound(eserviceId)); + }); + it("should throw eServiceModeNotAllowed if the eService mode is incorrect", async () => { + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForReceive.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + eServiceModeNotAllowed(eServiceReceive.id, "Deliver") + ); + + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeForDeliver.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + eServiceModeNotAllowed(eServiceDeliver.id, "Receive") + ); + }); + it("Should throw missingFreeOfChargeReason if the freeOfChargeReason is missing", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: { + ...purposeUpdateContent, + freeOfChargeReason: undefined, + }, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(missingFreeOfChargeReason()); + }); + it("Should throw tenantNotFound if the tenant does not exist", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeForReceive.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + }); + it("Should throw tenantKindNotFound if the tenant kind does not exist", async () => { + const mockTenant = { + ...tenant, + kind: undefined, + }; + + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: mockTenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const invalidRiskAnalysis: RiskAnalysis = { + ...validRiskAnalysis, + riskAnalysisForm: { + ...validRiskAnalysis.riskAnalysisForm, + version: "0", + }, + }; + + const mockPurposeUpdateContent: PurposeUpdateContent = { + ...purposeUpdateContent, + riskAnalysisForm: buildRiskAnalysisSeed(invalidRiskAnalysis), + }; + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: mockPurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) + ); + }); }); }); }); From 25d2d72ae6eaf49a1719ed636472477d6ecfd3a0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:00:00 +0200 Subject: [PATCH 202/537] Adjust to latest changes --- packages/purpose-process/src/model/domain/toEvent.ts | 4 +++- packages/purpose-process/src/services/purposeService.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index 20338304f0..a4f4ad498e 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -109,10 +109,12 @@ export const toCreateEventWaitingForApprovalPurposeDeleted = ({ export const toCreateEventPurposeArchived = ({ purpose, + purposeVersionId, version, correlationId, }: { purpose: Purpose; + purposeVersionId: PurposeVersionId; version: number; correlationId: string; }): CreateEvent => ({ @@ -121,7 +123,7 @@ export const toCreateEventPurposeArchived = ({ event: { type: "PurposeArchived", event_version: 2, - data: { purpose: toPurposeV2(purpose) }, + data: { purpose: toPurposeV2(purpose), versionId: purposeVersionId }, }, correlationId, }); diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ec179b3948..dfcd9a6558 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -383,6 +383,7 @@ export function purposeServiceBuilder( const event = toCreateEventPurposeArchived({ purpose: updatedPurpose, + purposeVersionId: archivedVersion.id, version: purpose.metadata.version, correlationId, }); From d4fbdf6ca1fad49b22019bb0e86190a180712e8d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:06:06 +0200 Subject: [PATCH 203/537] Fix import --- packages/purpose-process/src/services/purposeService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 3536bd2a7d..1375c0de54 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -24,7 +24,6 @@ import { PurposeRiskAnalysisForm, PurposeEvent, EServiceMode, - Ownership, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { From cfc8fe66fb8ef9fb8a5bc568a9f4194ab03ab1c7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:16:29 +0200 Subject: [PATCH 204/537] Remove comment --- packages/purpose-process/src/services/purposeService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 87c4d184fe..3788ede6bb 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -62,8 +62,6 @@ export function purposeServiceBuilder( _dbInstance: DB, readModelService: ReadModelService ) { - // const repository = eventRepository(dbInstance, purposeEventToBinaryData); - return { async getPurposeById( purposeId: PurposeId, From 9cbea121fac52b8548b54fe7da9ea8db2801e89a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:20:54 +0200 Subject: [PATCH 205/537] Fix typo --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 4113d314e4..5e2062689b 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -318,7 +318,7 @@ describe("database test", async () => { ) ); }); - it("should throw organizationNotAllowed if the requester is not the producer not the consumer", async () => { + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { const randomId: TenantId = generateId(); const mockDocument = getMockPurposeVersionDocument(); const mockPurpose1: Purpose = { From 9b2ab6c00950447ee4fa17882e2540717be0872e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:25:05 +0200 Subject: [PATCH 206/537] Simplify test --- .../test/purposeService.integration.test.ts | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 5e2062689b..4911959df3 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -226,27 +226,18 @@ describe("database test", async () => { }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const mockDocument = getMockPurposeVersionDocument(); - const mockPurpose1: Purpose = { - ...mockPurpose, - eserviceId: mockEService.id, - versions: [{ ...mockPurposeVersion, riskAnalysis: mockDocument }], - }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; - await addOnePurpose(mockPurpose2, postgresDB, purposes); + const notExistingId: PurposeId = generateId(); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose1.id, + purposeId: notExistingId, versionId: mockPurposeVersion.id, documentId: mockDocument.id, organizationId: mockEService.producerId, }) - ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); + ).rejects.toThrowError(purposeNotFound(notExistingId)); }); it("should throw eserviceNotFound if the eservice doesn't exist", async () => { const mockDocument = getMockPurposeVersionDocument(); From 14afff2aebf8fcd5a84ea4d08265d843ab83def3 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 17 Apr 2024 10:28:01 +0200 Subject: [PATCH 207/537] disable lint check on test --- packages/purpose-process/test/utils.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 56732ebaee..7c6eebeb96 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { PurposeCollection, riskAnalysisFormToRiskAnalysisFormToValidate, @@ -20,9 +21,9 @@ import { } from "pagopa-interop-models"; import { IDatabase } from "pg-promise"; import { - PurposeUpdateContent, - ReversePurposeUpdateContent, - RiskAnalysisFormSeed, + ApiPurposeUpdateContent, + ApiReversePurposeUpdateContent, + ApiRiskAnalysisFormSeed, } from "../src/model/domain/models.js"; export const addOnePurpose = async ( @@ -70,12 +71,14 @@ export const getMockEService = (): EService => ({ export const buildRiskAnalysisSeed = ( riskAnalysis: RiskAnalysis -): RiskAnalysisFormSeed => +): ApiRiskAnalysisFormSeed => riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysis.riskAnalysisForm); export const createUpdatedPurpose = ( mockPurpose: Purpose, - purposeUpdateContent: PurposeUpdateContent | ReversePurposeUpdateContent, + purposeUpdateContent: + | ApiPurposeUpdateContent + | ApiReversePurposeUpdateContent, mockValidRiskAnalysis: RiskAnalysis, writtenPayload: DraftPurposeUpdatedV2 ): Purpose => ({ From 2435409dcb6f1c457e7be7299055a78702e8faa1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:48:51 +0200 Subject: [PATCH 208/537] Simplify test --- .../purpose-process/test/purposeService.integration.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 4911959df3..080fee3242 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -225,7 +225,6 @@ describe("database test", async () => { expect(result).toEqual(mockDocument); }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { - const mockDocument = getMockPurposeVersionDocument(); const notExistingId: PurposeId = generateId(); await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); @@ -233,8 +232,8 @@ describe("database test", async () => { expect( purposeService.getRiskAnalysisDocument({ purposeId: notExistingId, - versionId: mockPurposeVersion.id, - documentId: mockDocument.id, + versionId: generateId(), + documentId: generateId(), organizationId: mockEService.producerId, }) ).rejects.toThrowError(purposeNotFound(notExistingId)); From 5a1c1e6a0029c00f1b975acec1030adb6c75eb78 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:51:43 +0200 Subject: [PATCH 209/537] Simplify test --- .../test/purposeService.integration.test.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index c2305b9fae..25f2b47dfa 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -388,27 +388,24 @@ describe("database test", async () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomId: PurposeId = generateId(); const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; - await addOnePurpose(mockPurpose2, postgresDB, purposes); + + await addOnePurpose(mockPurpose1, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: randomId, versionId: mockPurposeVersion.id, organizationId: mockEService.producerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); + ).rejects.toThrowError(purposeNotFound(randomId)); }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const randomVersionId: PurposeVersionId = generateId(); From 3b91dd85aa9598d7ac32add600dbb4db37951e67 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 10:53:35 +0200 Subject: [PATCH 210/537] Rename test suite --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index c3949c69d5..61acf59c34 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -52,7 +52,7 @@ import { } from "../src/model/domain/errors.js"; import { addOnePurpose, getMockEService } from "./utils.js"; -describe("database test", async () => { +describe("Integration tests", async () => { let purposes: PurposeCollection; let eservices: EServiceCollection; let tenants: TenantCollection; From 0eb6e32da0da0def2454685ac2dc339768756ba9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 11:36:39 +0200 Subject: [PATCH 211/537] Simplify test --- .../test/purposeService.integration.test.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index e386818c23..920d39cf1f 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -639,28 +639,25 @@ describe("database test", async () => { it("should throw purposeNotFound if the purpose doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); + const randomId: PurposeId = generateId(); const mockPurpose1: Purpose = { ...mockPurpose, eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; - await addOnePurpose(mockPurpose2, postgresDB, purposes); + + await addOnePurpose(mockPurpose1, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: randomId, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeNotFound(mockPurpose1.id)); + ).rejects.toThrowError(purposeNotFound(randomId)); }); it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { const mockEService = getMockEService(); From 58607f671a24a9f9926254399a4b89f577b979ed Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 11:39:20 +0200 Subject: [PATCH 212/537] Adjust test --- .../test/purposeService.integration.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index efeec906a5..44802d42af 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1342,14 +1342,17 @@ describe("database test", async () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); }); - it("should throw purposeNotFound if the purpose doesn't exist", () => { + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomId: PurposeId = generateId(); + + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.deletePurpose({ - purposeId: mockPurpose.id, + purposeId: randomId, organizationId: mockPurpose.consumerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeNotFound(mockPurpose.id)); + ).rejects.toThrowError(purposeNotFound(randomId)); }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { const mockEService = getMockEService(); From ac0aeee006f82019bec2126eaed72e72a2eee06a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 11:49:19 +0200 Subject: [PATCH 213/537] Add tests --- .../test/purposeService.integration.test.ts | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index c933a188e5..c65335fe7e 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1873,8 +1873,55 @@ describe("Integration tests", async () => { }) ).rejects.toThrowError(purposeNotFound(randomPurposeId)); }); - it("should throw purposeVersionNotFound if the purpose version doesn't exist", () => {}); - it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", () => {}); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const mockEService = getMockEService(); + const randomVersionId: PurposeVersionId = generateId(); + + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: randomVersionId, + organizationId: mockPurpose1.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose1.id, randomVersionId) + ); + }); + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { + const mockEService = getMockEService(); + const randomId: TenantId = generateId(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose1: Purpose = { + ...mockPurpose, + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + organizationId: randomId, + correlationId: generateId(), + }) + ).rejects.toThrowError(organizationNotAllowed(randomId)); + }); it("should throw notValidVersionState if the purpose version is in draft state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { @@ -1953,11 +2000,11 @@ describe("Integration tests", async () => { notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) ); }); - it("should throw notValidVersionState if the purpose version is in draft", async () => { + it("should throw notValidVersionState if the purpose version is in archived state", async () => { const mockEService = getMockEService(); const mockPurposeVersion: PurposeVersion = { ...getMockPurposeVersion(), - state: purposeVersionState.draft, + state: purposeVersionState.archived, }; const mockPurpose1: Purpose = { ...mockPurpose, From b6bc1637007773657f57453a8a65a1f6fa5375f3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 12:04:36 +0200 Subject: [PATCH 214/537] Adjust updatedAt --- .../src/services/purposeService.ts | 1 + .../test/purposeService.integration.test.ts | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 6633b4c01c..049e01ab3f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -191,6 +191,7 @@ export function purposeServiceBuilder( versions: purpose.data.versions.filter( (v) => v.id !== purposeVersion.id ), + updatedAt: new Date(), }; const event = toCreateEventWaitingForApprovalPurposeVersionDeleted({ diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 25f2b47dfa..ea4f661670 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -3,7 +3,15 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { + afterAll, + afterEach, + beforeAll, + describe, + expect, + it, + vi, +} from "vitest"; import { EServiceCollection, PurposeCollection, @@ -342,6 +350,9 @@ describe("database test", async () => { describe("deletePurposeVersion", () => { it("should write in event-store for the deletion of a purpose version", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + const mockPurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, @@ -383,9 +394,12 @@ describe("database test", async () => { const expectedPurpose: Purpose = { ...mockPurpose1, versions: [], + updatedAt: new Date(), }; expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const randomId: PurposeId = generateId(); From 39d85b9fae8f1cad2bc9695492c9b893c1141ef2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 12:07:47 +0200 Subject: [PATCH 215/537] Adjust updatedAt --- packages/purpose-process/src/services/purposeService.ts | 1 + packages/purpose-process/test/purposeService.integration.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 3c8e4f36f9..fa2660ece2 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -315,5 +315,6 @@ const replacePurposeVersion = ( return { ...purpose, versions: updatedVersions, + updatedAt: new Date(), }; }; diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 920d39cf1f..44ac15ede1 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -630,6 +630,7 @@ describe("database test", async () => { const expectedPurpose: Purpose = { ...mockPurpose1, versions: [expectedPurposeVersion], + updatedAt: new Date(), }; expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); From 67801543531c095f2c8cb5b155c7a052949bfa3d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 12:11:12 +0200 Subject: [PATCH 216/537] Remove not needed env vars --- packages/purpose-readmodel-writer/.env | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/purpose-readmodel-writer/.env b/packages/purpose-readmodel-writer/.env index 48e8edc45d..de8cdcc8bc 100644 --- a/packages/purpose-readmodel-writer/.env +++ b/packages/purpose-readmodel-writer/.env @@ -1,5 +1,3 @@ -HOST=0.0.0.0 -PORT=3000 LOG_LEVEL=info KAFKA_CLIENT_ID="purpose" @@ -13,5 +11,3 @@ READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 AWS_REGION="eu-central-1" - -WELL_KNOWN_URLS="https://dev.interop.pagopa.it/.well-known/jwks.json" From 204e1de4e43aa7e6d682bcb51150ff21c2873dde Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 17 Apr 2024 12:22:27 +0200 Subject: [PATCH 217/537] fixed missing updatedAt --- packages/purpose-process/src/services/purposeService.ts | 1 + packages/purpose-process/test/utils.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index fe41bcb270..cc2ac29eb2 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -443,6 +443,7 @@ const updatePurposeInternal = async ( const updatedPurpose: Purpose = { ...purpose.data, ...updateContent, + updatedAt: new Date(), riskAnalysisForm: newRiskAnalysis, }; diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 7c6eebeb96..87b379bad8 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -87,6 +87,7 @@ export const createUpdatedPurpose = ( description: purposeUpdateContent.description, isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, freeOfChargeReason: purposeUpdateContent.freeOfChargeReason, + updatedAt: new Date(Number(writtenPayload.purpose?.updatedAt)), riskAnalysisForm: { ...mockValidRiskAnalysis.riskAnalysisForm, id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), From 52b50cec9c635116e3db09f7be32643baf72182c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 12:39:22 +0200 Subject: [PATCH 218/537] Add to do --- packages/purpose-process/src/routers/PurposeRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 305fdbfbfa..2870ffb758 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -70,7 +70,7 @@ const purposeRouter = ( INTERNAL_ROLE, SUPPORT_ROLE, ]), - (_req, res) => res.status(501).send() + (_req, res) => res.status(501).send() // TO DO ) .post("/purposes", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => res.status(501).send() From 97da46370b26a56d8acd3e768106bb20b9b937b5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 13:59:45 +0200 Subject: [PATCH 219/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index e8f6590472..8ce38f9dd1 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -140,6 +140,10 @@ export function purposeServiceBuilder( documentId: PurposeVersionDocumentId; organizationId: TenantId; }): Promise { + logger.info( + `Retrieving Risk Analysis document ${documentId} in version ${versionId} of Purpose ${purposeId}` + ); + const purpose = await retrievePurpose(purposeId, readModelService); const eservice = await retrieveEService( purpose.data.eserviceId, From 7f0f1d434a484b4b72fa791050fa4beaa6180067 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 14:01:04 +0200 Subject: [PATCH 220/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d5f7284ca0..c50e6cee96 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -321,6 +321,8 @@ export function purposeServiceBuilder( organizationId: TenantId; correlationId: string; }): Promise { + logger.info(`Deleting Purpose ${purposeId}`); + const purpose = await retrievePurpose(purposeId, readModelService); assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); From d80c4479f996404f1cf1ad31a45af56aebfe7fa4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 14:01:37 +0200 Subject: [PATCH 221/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index dfcd9a6558..e3651330b8 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -353,6 +353,8 @@ export function purposeServiceBuilder( organizationId: TenantId; correlationId: string; }): Promise { + logger.info(`Archiving Version ${versionId} in Purpose ${purposeId}`); + const purpose = await retrievePurpose(purposeId, readModelService); assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); From faef6b23b804c9809ddbc2b78ce3a0439b682596 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 14:02:06 +0200 Subject: [PATCH 222/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1375c0de54..f72eef8d3b 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -406,6 +406,8 @@ export function purposeServiceBuilder( organizationId: TenantId; correlationId: string; }): Promise { + logger.info(`Suspending Version ${versionId} in Purpose ${purposeId}`); + const purpose = await retrievePurpose(purposeId, readModelService); const eservice = await retrieveEService( From 7b703abc3c21a3421b6e0f416579546d1b4831fe Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:13:43 +0200 Subject: [PATCH 223/537] Add converter --- .../purpose-process/src/model/domain/apiConverter.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index 865f37c210..421f8c2121 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -69,6 +69,18 @@ export const purposeVersionStateToApiPurposeVersionState = ( .with(purposeVersionState.waitingForApproval, () => "WAITING_FOR_APPROVAL") .exhaustive(); +export const apiPurposeVersionStateToPurposeVersionState = ( + state: ApiPurposeVersionState +): PurposeVersionState => + match(state) + .with("ACTIVE", () => purposeVersionState.active) + .with("ARCHIVED", () => purposeVersionState.archived) + .with("DRAFT", () => purposeVersionState.draft) + .with("REJECTED", () => purposeVersionState.rejected) + .with("SUSPENDED", () => purposeVersionState.suspended) + .with("WAITING_FOR_APPROVAL", () => purposeVersionState.waitingForApproval) + .exhaustive(); + export const purposeVersionDocumentToApiPurposeVersionDocument = ( document: PurposeVersionDocument ): ApiPurposeVersionDocument => ({ From 7cfb6f2ede6b985cd05c9760330123c70ae49095 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:13:51 +0200 Subject: [PATCH 224/537] Wrap filters --- .../purpose-process/src/model/domain/models.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 83c06470f9..f11f21ffd7 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -1,4 +1,9 @@ import { z } from "zod"; +import { + EServiceId, + PurposeVersionState, + TenantId, +} from "pagopa-interop-models"; import * as api from "../generated/api.js"; export type ApiRiskAnalysisForm = z.infer; @@ -22,3 +27,12 @@ export type RiskAnalysisFormSeed = z.infer< export type ReversePurposeUpdateContent = z.infer< typeof api.schemas.ReversePurposeUpdateContent >; + +export type ApiGetPurposesFilters = { + name?: string; + eservicesIds: EServiceId[]; + consumersIds: TenantId[]; + producersIds: TenantId[]; + states: PurposeVersionState[]; + excludeDraft: boolean | undefined; +}; From 3744f9e2efd4513d05d259b9b308037ce374eeab Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:14:03 +0200 Subject: [PATCH 225/537] Add readmodel function --- .../src/services/readModelService.ts | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index af22c02358..a297048419 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -4,6 +4,7 @@ import { EServiceCollection, TenantCollection, PurposeCollection, + ReadModelFilter, } from "pagopa-interop-commons"; import { EService, @@ -15,9 +16,12 @@ import { EServiceReadModel, Purpose, PurposeId, + ListResult, + purposeVersionState, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; +import { ApiGetPurposesFilters } from "../model/domain/models.js"; async function getPurpose( purposes: PurposeCollection, @@ -111,6 +115,128 @@ export function readModelServiceBuilder( ): Promise | undefined> { return getPurpose(purposes, { "data.id": id }); }, + async getPurposes( + filters: ApiGetPurposesFilters, + offset: number, + limit: number + ): Promise> { + const { + name, + eservicesIds, + consumersIds, + producersIds, + states, + excludeDraft, + } = filters; + + const nameFilter: ReadModelFilter = name + ? { + "data.title": { + $regex: ReadModelRepository.escapeRegExp(name), + $options: "i", + }, + } + : {}; + + const eservicesIdsFilter: ReadModelFilter = + ReadModelRepository.arrayToFilter(eservicesIds, { + "data.eserviceId": { $in: eservicesIds }, + }); + + const consumersIdsFilter: ReadModelFilter = + ReadModelRepository.arrayToFilter(consumersIds, { + "data.consumerId": { $in: consumersIds }, + }); + + const versionStateFilter: ReadModelFilter = + ReadModelRepository.arrayToFilter(states, { + "data.versions.state": { $in: states }, + }); + + const draftFilter: ReadModelFilter = excludeDraft + ? { + $nor: [ + { "data.versions": { $size: 0 } }, + { + $and: [ + { "data.versions": { $size: 1 } }, + { + "data.descriptors.state": { + $eq: purposeVersionState.draft, + }, + }, + ], + }, + ], + } + : {}; + + const aggregationPipeline = [ + { + $match: { + ...nameFilter, + ...eservicesIdsFilter, + ...consumersIdsFilter, + ...versionStateFilter, + ...draftFilter, + } satisfies ReadModelFilter, + }, + producersIds.length > 0 + ? [ + { + $lookup: { + from: "eservices", + localField: "data.eserviceId", + foreignField: "data.id", + as: "eservices", + }, + }, + { $unwind: "$eservices" }, + { + $match: { + "data.producerId": { $in: producersIds }, + }, + }, + ] + : [], + { + $project: { + data: 1, + computedColumn: { $toLower: ["$data.name"] }, + }, + }, + { + $sort: { computedColumn: 1 }, + }, + ]; + + const data = await purposes + .aggregate([ + ...aggregationPipeline, + { $skip: offset }, + { $limit: limit }, + ]) + .toArray(); + + const result = z.array(Purpose).safeParse(data.map((d) => d.data)); + if (!result.success) { + logger.error( + `Unable to parse purposes items: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + + throw genericError("Unable to parse purposes items"); + } + + return { + results: result.data, + totalCount: await ReadModelRepository.getTotalCount( + purposes, + aggregationPipeline + ), + }; + }, }; } From 19eec3e59d8237e449ca5f95f766e072b1548a84 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:14:31 +0200 Subject: [PATCH 226/537] Implement endpoint --- .../src/routers/PurposeRouter.ts | 42 ++++++++++++++++++- .../src/services/purposeService.ts | 32 ++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 2870ffb758..95b02fc225 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -8,9 +8,10 @@ import { ReadModelRepository, initDB, } from "pagopa-interop-commons"; -import { unsafeBrandId } from "pagopa-interop-models"; +import { EServiceId, TenantId, unsafeBrandId } from "pagopa-interop-models"; import { api } from "../model/generated/api.js"; import { + apiPurposeVersionStateToPurposeVersionState, purposeToApiPurpose, purposeVersionDocumentToApiPurposeVersionDocument, purposeVersionToApiPurposeVersion, @@ -70,7 +71,44 @@ const purposeRouter = ( INTERNAL_ROLE, SUPPORT_ROLE, ]), - (_req, res) => res.status(501).send() // TO DO + async (req, res) => { + try { + const { + name, + eservicesIds, + consumersIds, + producersIds, + states, + excludeDraft, + offset, + limit, + } = req.query; + const purposes = await purposeService.getPurposes( + { + name, + eservicesIds: eservicesIds.map(unsafeBrandId), + consumersIds: consumersIds.map(unsafeBrandId), + producersIds: producersIds.map(unsafeBrandId), + states: states.map(apiPurposeVersionStateToPurposeVersionState), + excludeDraft, + }, + offset, + limit + ); + return res + .status(200) + .json({ + results: purposes.results.map((purpose) => + purposeToApiPurpose(purpose, false) + ), + totalCount: purposes.totalCount, + }) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, getPurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post("/purposes", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => res.status(501).send() diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d9c4c8a0d4..689574dc57 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -25,6 +25,7 @@ import { PurposeEvent, EServiceMode, Ownership, + ListResult, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -49,6 +50,7 @@ import { toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { + ApiGetPurposesFilters, PurposeUpdateContent, ReversePurposeUpdateContent, } from "../model/domain/models.js"; @@ -475,6 +477,36 @@ export function purposeServiceBuilder( await repository.createEvent(event); return suspendedPurposeVersion; }, + async getPurposes( + filters: ApiGetPurposesFilters, + offset: number, + limit: number + ): Promise> { + logger.info("To DO"); + + const purposesList = await readModelService.getPurposes( + filters, + offset, + limit + ); + + const purposesToReturn: Purpose[] = purposesList.results.map( + (purpose) => ({ + ...purpose, + versions: filters.excludeDraft + ? purpose.versions.filter( + (version) => version.state !== purposeVersionState.draft + ) + : purpose.versions, + riskAnalysisForm: undefined, + }) + ); + + return { + results: purposesToReturn, + totalCount: purposesList.totalCount, + }; + }, }; } From 81972948c459ea88ae1813ffc07b0e0ab260d4a9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:36:49 +0200 Subject: [PATCH 227/537] Fix test suite structure --- .../test/purposeService.integration.test.ts | 405 +++++++++--------- 1 file changed, 207 insertions(+), 198 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a1d6748f65..2ef9531dab 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -984,216 +984,225 @@ describe("database test", async () => { expect(1).toBe(1); }); }); - }); - describe("updatePurpose and reverseUpdatePurpose", () => { - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { - const consumerTenantKind = randomArrayItem(Object.values(tenantKind)); - const consumer: Tenant = { - ...getMockTenant(), - kind: consumerTenantKind, - }; - - const mockEservice: EService = { ...getMockEService(), mode: "Deliver" }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEservice.id, - consumerId: consumer.id, - versions: [ - { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, + + describe("updatePurpose and reverseUpdatePurpose", () => { + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + const consumerTenantKind = randomArrayItem(Object.values(tenantKind)); + const consumer: Tenant = { + ...getMockTenant(), + kind: consumerTenantKind, + }; + + const mockEservice: EService = { + ...getMockEService(), + mode: "Deliver", + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEservice.id, + consumerId: consumer.id, + versions: [ + { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }, + ], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(consumer, tenants); + + const mockValidRiskAnalysis = + getMockValidRiskAnalysis(consumerTenantKind); + + const purposeUpdateContent: PurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + riskAnalysisForm: buildRiskAnalysisSeed(mockValidRiskAnalysis), + }; + + await purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: consumer.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + title: purposeUpdateContent.title, + description: purposeUpdateContent.description, + isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), }, - ], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(consumer, tenants); - - const mockValidRiskAnalysis = - getMockValidRiskAnalysis(consumerTenantKind); - - const purposeUpdateContent: PurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: false, - riskAnalysisForm: buildRiskAnalysisSeed(mockValidRiskAnalysis), - }; - - await purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: consumer.id, - correlationId: generateId(), - }); + }; - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); + it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + const producerTenantKind = randomArrayItem(Object.values(tenantKind)); + const producer: Tenant = { + ...getMockTenant(), + kind: producerTenantKind, + }; - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, - }); + const mockEservice: EService = { + ...getMockEService(), + mode: "Receive", + producerId: producer.id, + }; - const expectedPurpose: Purpose = { - ...mockPurpose, - title: purposeUpdateContent.title, - description: purposeUpdateContent.description, - isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( - (sa) => sa.key === singleAnswer.key - )!.id - ), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( - (ma) => ma.key === multiAnswer.key - )!.id - ), - }) - ), - }, - }; + const mockValidRiskAnalysis = + getMockValidRiskAnalysis(producerTenantKind); - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - }); - it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { - const producerTenantKind = randomArrayItem(Object.values(tenantKind)); - const producer: Tenant = { - ...getMockTenant(), - kind: producerTenantKind, - }; - - const mockEservice: EService = { - ...getMockEService(), - mode: "Receive", - producerId: producer.id, - }; - - const mockValidRiskAnalysis = - getMockValidRiskAnalysis(producerTenantKind); - - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEservice.id, - consumerId: producer.id, - versions: [ - { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEservice.id, + consumerId: producer.id, + versions: [ + { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }, + ], + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: generateId(), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: generateId(), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: generateId(), + }) + ), }, - ], - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: generateId(), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: generateId(), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: generateId(), - }) - ), - }, - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(producer, tenants); - - const reversePurposeUpdateContent: ReversePurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: false, - }; - - await purposeService.updateReversePurpose({ - purposeId: mockPurpose.id, - reversePurposeUpdateContent, - organizationId: producer.id, - correlationId: generateId(), - }); + }; - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, - }); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(producer, tenants); + + const reversePurposeUpdateContent: ReversePurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + }; - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, - }); + await purposeService.updateReversePurpose({ + purposeId: mockPurpose.id, + reversePurposeUpdateContent, + organizationId: producer.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); - const expectedPurpose: Purpose = { - ...mockPurpose, - title: reversePurposeUpdateContent.title, - description: reversePurposeUpdateContent.description, - isFreeOfCharge: reversePurposeUpdateContent.isFreeOfCharge, - riskAnalysisForm: { - ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), - singleAnswers: - mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( - (singleAnswer) => ({ - ...singleAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( - (sa) => sa.key === singleAnswer.key - )!.id - ), - }) - ), - multiAnswers: mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( - (multiAnswer) => ({ - ...multiAnswer, - id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( - (ma) => ma.key === multiAnswer.key - )!.id + const expectedPurpose: Purpose = { + ...mockPurpose, + title: reversePurposeUpdateContent.title, + description: reversePurposeUpdateContent.description, + isFreeOfCharge: reversePurposeUpdateContent.isFreeOfCharge, + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) ), - }) - ), - }, - }; + }, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + }); - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); }); }); From 593cb03b8fae0fa6be54d36f9eb453192f125688 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:38:38 +0200 Subject: [PATCH 228/537] Add test scaffold --- .../test/purposeService.integration.test.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 2ef9531dab..ae6764ae11 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1203,6 +1203,34 @@ describe("database test", async () => { }); }); + describe("getPurposes", () => { + it("should get the purposes if they exist (parameters: name)", () => { + expect(1).toBe(1); + }); + it("should get the purposes if they exist (parameters: eservicesIds)", () => { + expect(1).toBe(1); + }); + it("should get the purposes if they exist (parameters: consumersIds)", () => { + expect(1).toBe(1); + }); + it("should get the purposes if they exist (parameters: producersIds)", () => { + expect(1).toBe(1); + }); + it("should get the purposes if they exist (parameters: states)", () => { + expect(1).toBe(1); + }); + it("should not include purposes whose only version is draft (excludeDraft = true)", () => { + expect(1).toBe(1); + }); + it("should include purposes whose only version is draft (excludeDraft = false)", () => { + expect(1).toBe(1); + }); + it("should not filter out draft versions if the purpose has both draft and non-draft ones (excludeDraft = false)", () => { + expect(1).toBe(1); + }); + it("should filter out draft versions if the purpose has both draft and non-draft ones (excludeDraft = true)", () => { + expect(1).toBe(1); + }); }); }); }); From 43d5bc74f4bb4ee8dc223d2a6768b2e03e3b6d59 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 17 Apr 2024 16:46:16 +0200 Subject: [PATCH 229/537] removed unused dependencies --- packages/purpose-process/package.json | 2 -- pnpm-lock.yaml | 6 ------ 2 files changed, 8 deletions(-) diff --git a/packages/purpose-process/package.json b/packages/purpose-process/package.json index f8a2f130f6..3ee848d0d2 100644 --- a/packages/purpose-process/package.json +++ b/packages/purpose-process/package.json @@ -23,7 +23,6 @@ "@types/dotenv-flow": "3.2.0", "@types/express": "4.17.17", "@types/node": "20.3.1", - "@types/uuid": "9.0.2", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.2.2", @@ -42,7 +41,6 @@ "pagopa-interop-models": "workspace:*", "pg-promise": "11.5.0", "ts-pattern": "5.0.6", - "uuid": "9.0.0", "zod": "3.21.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8f41c7b6a..d5294fa36f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -905,9 +905,6 @@ importers: ts-pattern: specifier: 5.0.6 version: 5.0.6 - uuid: - specifier: 9.0.0 - version: 9.0.0 zod: specifier: 3.21.4 version: 3.21.4 @@ -924,9 +921,6 @@ importers: '@types/node': specifier: 20.3.1 version: 20.3.1 - '@types/uuid': - specifier: 9.0.2 - version: 9.0.2 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test From d6a3b882bdc427d81fb75d5633f896dc1e23e53c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:53:04 +0200 Subject: [PATCH 230/537] Minor improvement --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a3a820bc6c..332d759f13 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -320,6 +320,6 @@ const replacePurposeVersion = ( return { ...purpose, versions: updatedVersions, - updatedAt: new Date(), + updatedAt: newVersion.updatedAt, }; }; From d49baf57774311b560754c2ef4bee8b98da42a9c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 16:58:12 +0200 Subject: [PATCH 231/537] Refactor --- .../src/services/purposeService.ts | 31 ++----------------- .../src/services/validators.ts | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 packages/purpose-process/src/services/validators.ts diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 3788ede6bb..fdfee2c0d1 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,9 +1,4 @@ -import { - DB, - logger, - riskAnalysisFormToRiskAnalysisFormToValidate, - validateRiskAnalysis, -} from "pagopa-interop-commons"; +import { DB, logger } from "pagopa-interop-commons"; import { EService, EServiceId, @@ -13,8 +8,6 @@ import { Purpose, PurposeId, TenantKind, - purposeVersionState, - RiskAnalysisForm, } from "pagopa-interop-models"; import { eserviceNotFound, @@ -23,6 +16,7 @@ import { tenantNotFound, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; +import { isRiskAnalysisFormValid, purposeIsDraft } from "./validators.js"; const retrievePurpose = async ( purposeId: PurposeId, @@ -121,24 +115,3 @@ const authorizeRiskAnalysisForm = ({ }; } }; - -const isRiskAnalysisFormValid = ( - riskAnalysisForm: RiskAnalysisForm | undefined, - schemaOnlyValidation: boolean, - tenantKind: TenantKind -): boolean => { - if (riskAnalysisForm === undefined) { - return false; - } else { - return ( - validateRiskAnalysis( - riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), - schemaOnlyValidation, - tenantKind - ).type === "valid" - ); - } -}; - -const purposeIsDraft = (purpose: Purpose): boolean => - !purpose.versions.some((v) => v.state !== purposeVersionState.draft); diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts new file mode 100644 index 0000000000..4c9c595753 --- /dev/null +++ b/packages/purpose-process/src/services/validators.ts @@ -0,0 +1,31 @@ +import { + riskAnalysisFormToRiskAnalysisFormToValidate, + validateRiskAnalysis, +} from "pagopa-interop-commons"; +import { + Purpose, + RiskAnalysisForm, + TenantKind, + purposeVersionState, +} from "pagopa-interop-models"; + +export const isRiskAnalysisFormValid = ( + riskAnalysisForm: RiskAnalysisForm | undefined, + schemaOnlyValidation: boolean, + tenantKind: TenantKind +): boolean => { + if (riskAnalysisForm === undefined) { + return false; + } else { + return ( + validateRiskAnalysis( + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), + schemaOnlyValidation, + tenantKind + ).type === "valid" + ); + } +}; + +export const purposeIsDraft = (purpose: Purpose): boolean => + !purpose.versions.some((v) => v.state !== purposeVersionState.draft); From 0c4d7d64f7b90cf6923214213a39e920092c17f4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 17:01:08 +0200 Subject: [PATCH 232/537] Fix lint --- packages/purpose-process/src/services/purposeService.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 19e46fb0b3..60b801f87f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,8 +1,4 @@ -import { - DB, - eventRepository, - logger, -} from "pagopa-interop-commons"; +import { DB, eventRepository, logger } from "pagopa-interop-commons"; import { EService, EServiceId, From c885a2a75505a9976b6f7bbf64e75cb8939374fd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 17:01:27 +0200 Subject: [PATCH 233/537] Fix import --- packages/purpose-process/src/services/purposeService.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 60b801f87f..684127aa08 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -8,6 +8,14 @@ import { Purpose, PurposeId, TenantKind, + PurposeVersionId, + Ownership, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionDocumentId, + ownership, + purposeEventToBinaryData, + purposeVersionState, } from "pagopa-interop-models"; import { eserviceNotFound, From 376974daf9822722a472bb05ed997ec3ef6a3319 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 17:02:14 +0200 Subject: [PATCH 234/537] Fix import --- packages/purpose-process/src/services/purposeService.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 7218eb2472..8fbe9e99b6 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -8,6 +8,12 @@ import { Purpose, PurposeId, TenantKind, + Ownership, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionDocumentId, + PurposeVersionId, + ownership, } from "pagopa-interop-models"; import { eserviceNotFound, From ffc294b229c0675fc3c4a1d7ce086f4d063307e4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 17:35:04 +0200 Subject: [PATCH 235/537] Improve test --- .../purpose-process/test/purposeService.integration.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 61acf59c34..356de1db95 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -136,10 +136,12 @@ describe("Integration tests", async () => { }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const notExistingId: PurposeId = generateId(); + const mockTenant = getMockTenant(); await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); expect( - purposeService.getPurposeById(notExistingId, generateId()) + purposeService.getPurposeById(notExistingId, mockTenant.id) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); it("should throw eserviceNotFound if the eservice doesn't exist", async () => { From b20bb9d46c4fa5a1688b065ed6beb3a27c23740b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 17 Apr 2024 17:45:40 +0200 Subject: [PATCH 236/537] Refactor tests --- .../test/purposeService.integration.test.ts | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 356de1db95..5bcdf0e918 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -16,7 +16,6 @@ import { IDatabase } from "pg-promise"; import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, - getMockAuthData, getMockPurpose, getMockTenant, mongoDBContainer, @@ -25,12 +24,10 @@ import { } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; import { - EService, EServiceId, Purpose, PurposeId, TenantId, - TenantKind, generateId, tenantKind, toReadModelEService, @@ -102,8 +99,6 @@ describe("Integration tests", async () => { }); describe("Purpose service", () => { - const mockPurpose = getMockPurpose(); - const mockEService = getMockEService(); describe("getPurposeById", () => { it("should get the purpose if it exists", async () => { const mockTenant = { @@ -111,8 +106,9 @@ describe("Integration tests", async () => { kind: tenantKind.PA, }; + const mockEService = getMockEService(); const mockPurpose1: Purpose = { - ...mockPurpose, + ...getMockPurpose(), eserviceId: mockEService.id, }; const mockPurpose2: Purpose = { @@ -137,6 +133,7 @@ describe("Integration tests", async () => { it("should throw purposeNotFound if the purpose doesn't exist", async () => { const notExistingId: PurposeId = generateId(); const mockTenant = getMockTenant(); + const mockPurpose = getMockPurpose(); await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); @@ -151,44 +148,46 @@ describe("Integration tests", async () => { kind: tenantKind.PA, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: notExistingId, }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); expect( - purposeService.getPurposeById(mockPurpose1.id, mockTenant.id) + purposeService.getPurposeById(mockPurpose.id, mockTenant.id) ).rejects.toThrowError(eserviceNotFound(notExistingId)); }); it("should throw tenantNotFound if the tenant doesn't exist", async () => { const notExistingId: TenantId = generateId(); + const mockEService = getMockEService(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( - purposeService.getPurposeById(mockPurpose1.id, notExistingId) + purposeService.getPurposeById(mockPurpose.id, notExistingId) ).rejects.toThrowError(tenantNotFound(notExistingId)); }); it("should throw tenantKindNotFound if the tenant doesn't exist", async () => { const mockTenant = getMockTenant(); + const mockEService = getMockEService(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(mockTenant, tenants); expect( - purposeService.getPurposeById(mockPurpose1.id, mockTenant.id) + purposeService.getPurposeById(mockPurpose.id, mockTenant.id) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); }); From 97391739c81931189d388d034fcb41a9b3abee26 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 09:25:06 +0200 Subject: [PATCH 237/537] Adjust tests --- .../test/purposeService.integration.test.ts | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 57da5729e5..f117c5fa65 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -613,17 +613,17 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, @@ -631,13 +631,13 @@ describe("Integration tests", async () => { }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "PurposeVersionRejected", event_version: 2, @@ -655,7 +655,7 @@ describe("Integration tests", async () => { updatedAt: new Date(), }; const expectedPurpose: Purpose = { - ...mockPurpose1, + ...mockPurpose, versions: [expectedPurposeVersion], updatedAt: new Date(), }; @@ -668,13 +668,13 @@ describe("Integration tests", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const randomId: PurposeId = generateId(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( @@ -690,20 +690,20 @@ describe("Integration tests", async () => { it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); @@ -711,21 +711,21 @@ describe("Integration tests", async () => { it("should throw organizationIsNotTheProducer if the requester is not the producer", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -736,25 +736,25 @@ describe("Integration tests", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const randomVersionId: PurposeVersionId = generateId(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: randomVersionId, rejectionReason: "test", organizationId: mockEService.producerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionNotFound(mockPurpose1.id, randomVersionId) + purposeVersionNotFound(mockPurpose.id, randomVersionId) ); }); it("should throw notValidVersionState if the purpose version is in draft state", async () => { @@ -763,18 +763,18 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, @@ -790,18 +790,18 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, @@ -817,18 +817,18 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.archived, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, @@ -844,18 +844,18 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.rejected, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, @@ -872,18 +872,18 @@ describe("Integration tests", async () => { state: purposeVersionState.suspended, suspendedAt: new Date(), }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.rejectPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, rejectionReason: "test", organizationId: mockEService.producerId, From 389d690cf5d618e16e57e2928d34f59a9bf09571 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 09:44:04 +0200 Subject: [PATCH 238/537] Adjust tests --- .../test/purposeService.integration.test.ts | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 44802d42af..6500671ac7 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1225,29 +1225,29 @@ describe("database test", async () => { describe("deletePurpose", () => { it("should write on event-store for the deletion of a purpose (no versions)", async () => { const mockEService = getMockEService(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.deletePurpose({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "DraftPurposeDeleted", event_version: 2, @@ -1258,7 +1258,7 @@ describe("database test", async () => { payload: writtenEvent.data, }); - expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); }); it("should write on event-store for the deletion of a purpose (draft version)", async () => { const mockEService = getMockEService(); @@ -1266,29 +1266,29 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.deletePurpose({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "DraftPurposeDeleted", event_version: 2, @@ -1299,7 +1299,7 @@ describe("database test", async () => { payload: writtenEvent.data, }); - expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); }); it("should write on event-store for the deletion of a purpose (waiting for approval version)", async () => { const mockEService = getMockEService(); @@ -1307,29 +1307,29 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.deletePurpose({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "WaitingForApprovalPurposeDeleted", event_version: 2, @@ -1340,7 +1340,7 @@ describe("database test", async () => { payload: writtenEvent.data, }); - expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose1)); + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const randomId: PurposeId = generateId(); @@ -1360,18 +1360,18 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurpose({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, organizationId: mockEService.producerId, correlationId: generateId(), }) @@ -1385,22 +1385,22 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurpose({ - purposeId: mockPurpose1.id, - organizationId: mockPurpose1.consumerId, + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); }); it("should throw purposeCannotBeDeleted if the purpose has a rejected version ", async () => { const mockEService = getMockEService(); @@ -1408,22 +1408,22 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.rejected, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurpose({ - purposeId: mockPurpose1.id, - organizationId: mockPurpose1.consumerId, + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); }); it("should throw purposeCannotBeDeleted if the purpose has a suspeneded version ", async () => { const mockEService = getMockEService(); @@ -1432,22 +1432,22 @@ describe("database test", async () => { state: purposeVersionState.suspended, suspendedAt: new Date(), }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurpose({ - purposeId: mockPurpose1.id, - organizationId: mockPurpose1.consumerId, + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); }); it("should throw purposeCannotBeDeleted if the purpose has an archived version ", async () => { const mockEService = getMockEService(); @@ -1455,22 +1455,22 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.archived, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.deletePurpose({ - purposeId: mockPurpose1.id, - organizationId: mockPurpose1.consumerId, + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose1.id)); + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); }); }); }); From cda09c71d22506117a06fa8382260035f69e7abe Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 09:47:27 +0200 Subject: [PATCH 239/537] Adjust tests --- .../test/purposeService.integration.test.ts | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ce000b64d2..cd0b390623 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1484,34 +1484,34 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "PurposeArchived", event_version: 2, }); const expectedPurpose: Purpose = { - ...mockPurpose1, + ...mockPurpose, versions: [ { ...mockPurposeVersion, @@ -1542,34 +1542,34 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [mockPurposeVersion1, mockPurposeVersion2], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion1.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "PurposeArchived", event_version: 2, }); const expectedPurpose: Purpose = { - ...mockPurpose1, + ...mockPurpose, versions: [ { ...mockPurposeVersion1, @@ -1591,6 +1591,7 @@ describe("database test", async () => { it("should throw purposeNotFound if the purpose doesn't exist", async () => { const randomPurposeId: PurposeId = generateId(); const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose = getMockPurpose(); await addOnePurpose(mockPurpose, postgresDB, purposes); expect( @@ -1609,16 +1610,16 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, organizationId: randomOrganizationId, correlationId: generateId(), @@ -1629,22 +1630,22 @@ describe("database test", async () => { }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const randomVersionId: PurposeVersionId = generateId(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: randomVersionId, organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionNotFound(mockPurpose1.id, randomVersionId) + purposeVersionNotFound(mockPurpose.id, randomVersionId) ); }); it("should throw notValidVersionState if the purpose version is in draft state", async () => { @@ -1652,18 +1653,18 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -1675,18 +1676,18 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -1698,18 +1699,18 @@ describe("database test", async () => { ...getMockPurposeVersion(), state: purposeVersionState.rejected, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); expect( purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( From ce1885d41f5e0283055594b165606b2cf558b81d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 09:57:52 +0200 Subject: [PATCH 240/537] Adjust tests --- .../src/services/purposeService.ts | 1 + .../test/purposeService.integration.test.ts | 95 ++++++++++--------- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 265bb36542..48215a6377 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -443,6 +443,7 @@ export function purposeServiceBuilder( ...purposeVersion, state: purposeVersionState.suspended, suspendedAt: new Date(), + updatedAt: new Date(), }; const event = match(suspender) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 50ce194809..a827cecd02 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1512,6 +1512,7 @@ describe("Integration tests", async () => { updatedAt: new Date(), }, ], + updatedAt: new Date(), }; const writtenPayload = decodeProtobufPayload({ @@ -1570,6 +1571,7 @@ describe("Integration tests", async () => { updatedAt: new Date(), }, ], + updatedAt: new Date(), }; const writtenPayload = decodeProtobufPayload({ @@ -1722,43 +1724,45 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion1], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion1.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "PurposeVersionSuspendedByConsumer", event_version: 2, }); const expectedPurpose: Purpose = { - ...mockPurpose1, + ...mockPurpose, versions: [ { ...mockPurposeVersion1, state: purposeVersionState.suspended, suspendedAt: new Date(), + updatedAt: new Date(), }, ], + updatedAt: new Date(), suspendedByConsumer: true, }; @@ -1780,44 +1784,46 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion1], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion1.id, organizationId: mockEService.producerId, correlationId: generateId(), }); const writtenEvent = await readLastEventByStreamId( - mockPurpose1.id, + mockPurpose.id, "purpose", postgresDB ); expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose1.id, + stream_id: mockPurpose.id, version: "1", type: "PurposeVersionSuspendedByProducer", event_version: 2, }); const expectedPurpose: Purpose = { - ...mockPurpose1, + ...mockPurpose, versions: [ { ...mockPurposeVersion1, state: purposeVersionState.suspended, suspendedAt: new Date(), + updatedAt: new Date(), }, ], suspendedByProducer: true, + updatedAt: new Date(), }; const writtenPayload = decodeProtobufPayload({ @@ -1832,6 +1838,7 @@ describe("Integration tests", async () => { it("should throw purposeNotFound if the purpose doesn't exist", async () => { const randomPurposeId: PurposeId = generateId(); const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose = getMockPurpose(); await addOnePurpose(mockPurpose, postgresDB, purposes); expect( @@ -1847,24 +1854,24 @@ describe("Integration tests", async () => { const mockEService = getMockEService(); const randomVersionId: PurposeVersionId = generateId(); - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: randomVersionId, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( - purposeVersionNotFound(mockPurpose1.id, randomVersionId) + purposeVersionNotFound(mockPurpose.id, randomVersionId) ); }); it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { @@ -1874,18 +1881,18 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.active, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, organizationId: randomId, correlationId: generateId(), @@ -1898,20 +1905,20 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.draft, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -1924,20 +1931,20 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.waitingForApproval, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.archivePurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -1950,20 +1957,20 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.rejected, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( @@ -1976,20 +1983,20 @@ describe("Integration tests", async () => { ...getMockPurposeVersion(), state: purposeVersionState.archived, }; - const mockPurpose1: Purpose = { - ...mockPurpose, + const mockPurpose: Purpose = { + ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); expect( purposeService.suspendPurposeVersion({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, - organizationId: mockPurpose1.consumerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), }) ).rejects.toThrowError( From 001b8f12582a46e89565c40b77eeb1f628f75bd7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 10:02:06 +0200 Subject: [PATCH 241/537] Fix tests --- .../purpose-process/test/purposeService.integration.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a840b9fdb7..695d016bae 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1510,6 +1510,7 @@ describe("Integration tests", async () => { updatedAt: new Date(), }, ], + updatedAt: new Date(), }; const writtenPayload = decodeProtobufPayload({ @@ -1568,6 +1569,7 @@ describe("Integration tests", async () => { updatedAt: new Date(), }, ], + updatedAt: new Date(), }; const writtenPayload = decodeProtobufPayload({ From 7045028dc54c044223351cf00820bfca01dcc402 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 10:07:56 +0200 Subject: [PATCH 242/537] Edit test title --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index b881a441d3..b3398c9513 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1292,7 +1292,7 @@ describe("Integration tests", async () => { expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); }); - it("should write on event-store for the deletion of a purpose (waiting for approval version)", async () => { + it("should write on event-store for the deletion of a purpose (waitingForApproval version)", async () => { const mockEService = getMockEService(); const mockPurposeVersion = { ...getMockPurposeVersion(), From 003b27655715cb95d3bc94328d80465a9b3d37d8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 10:09:01 +0200 Subject: [PATCH 243/537] Edit test title --- .../purpose-process/test/purposeService.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 695d016bae..60cb92606a 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1522,7 +1522,7 @@ describe("Integration tests", async () => { vi.useRealTimers(); }); - it("should write on event-store for the archiving of a purpose version, and delete waitingForApprovalVersions", async () => { + it("should write on event-store for the archiving of a purpose version, and delete waitingForApproval versions", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From 414f2c8133ab38a7b2da421f6394b02bcbf2392e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 16:09:00 +0200 Subject: [PATCH 244/537] Refactor --- packages/purpose-process/src/routers/PurposeRouter.ts | 3 +-- packages/purpose-process/src/services/purposeService.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 95b02fc225..f6b67a24f4 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -92,8 +92,7 @@ const purposeRouter = ( states: states.map(apiPurposeVersionStateToPurposeVersionState), excludeDraft, }, - offset, - limit + { offset, limit } ); return res .status(200) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 689574dc57..67ab04b510 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -479,8 +479,7 @@ export function purposeServiceBuilder( }, async getPurposes( filters: ApiGetPurposesFilters, - offset: number, - limit: number + { offset, limit }: { offset: number; limit: number } ): Promise> { logger.info("To DO"); From ad75a73f4e64115f7e6b16cbb419592f9d4e8b52 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 16:09:09 +0200 Subject: [PATCH 245/537] Fix --- packages/purpose-process/src/services/readModelService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index a297048419..c271d0afe2 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -161,7 +161,7 @@ export function readModelServiceBuilder( $and: [ { "data.versions": { $size: 1 } }, { - "data.descriptors.state": { + "data.versions.state": { $eq: purposeVersionState.draft, }, }, From ef20c38ee3891709060f85a0bfcf80044b84c726 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 16:09:22 +0200 Subject: [PATCH 246/537] Temporarily remove filter --- .../src/services/readModelService.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index c271d0afe2..64afb2bf87 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -181,24 +181,24 @@ export function readModelServiceBuilder( ...draftFilter, } satisfies ReadModelFilter, }, - producersIds.length > 0 - ? [ - { - $lookup: { - from: "eservices", - localField: "data.eserviceId", - foreignField: "data.id", - as: "eservices", - }, - }, - { $unwind: "$eservices" }, - { - $match: { - "data.producerId": { $in: producersIds }, - }, - }, - ] - : [], + // producersIds.length > 0 + // ? [ + // { + // $lookup: { + // from: "eservices", + // localField: "data.eserviceId", + // foreignField: "data.id", + // as: "eservices", + // }, + // }, + // { $unwind: "$eservices" }, + // { + // $match: { + // "data.producerId": { $in: producersIds }, + // }, + // }, + // ] + // : [], { $project: { data: 1, From 3c5b28143e2334a734f16296dd8df07c36b02d85 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 16:09:25 +0200 Subject: [PATCH 247/537] Add tests --- .../test/purposeService.integration.test.ts | 267 ++++++++++++++++-- 1 file changed, 248 insertions(+), 19 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ae6764ae11..f0cf7e2bdd 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -7,6 +7,7 @@ import { afterAll, afterEach, beforeAll, + beforeEach, describe, expect, it, @@ -1203,33 +1204,261 @@ describe("database test", async () => { }); }); - describe("getPurposes", () => { - it("should get the purposes if they exist (parameters: name)", () => { - expect(1).toBe(1); + describe("getPurposes", async () => { + const producerId1: TenantId = generateId(); + const producerId2: TenantId = generateId(); + const consumerId1: TenantId = generateId(); + + const mockEService1ByTenant1: EService = { + ...getMockEService(), + producerId: producerId1, + }; + + const mockEService2ByTenant1: EService = { + ...getMockEService(), + producerId: producerId1, + }; + + const mockEService3ByTenant2: EService = { + ...getMockEService(), + producerId: producerId2, + }; + + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + title: "purpose 1 - test", + eserviceId: mockEService1ByTenant1.id, + versions: [mockPurposeVersion1], + }; + + const mockPurposeVersion2: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + title: "purpose 2", + eserviceId: mockEService1ByTenant1.id, + versions: [mockPurposeVersion2], + }; + + const mockPurposeVersion3: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose3: Purpose = { + ...getMockPurpose(), + title: "purpose 3", + eserviceId: mockEService2ByTenant1.id, + versions: [mockPurposeVersion3], + }; + + const mockPurposeVersion4: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose4: Purpose = { + ...getMockPurpose(), + title: "purpose 4", + eserviceId: mockEService3ByTenant2.id, + versions: [mockPurposeVersion4], + }; + + const mockPurposeVersion5: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose5: Purpose = { + ...getMockPurpose(), + title: "purpose 5", + consumerId: consumerId1, + versions: [mockPurposeVersion5], + }; + + const mockPurposeVersion6_1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; + const mockPurposeVersion6_2: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose6: Purpose = { + ...getMockPurpose(), + title: "purpose 6", + consumerId: consumerId1, + versions: [mockPurposeVersion6_1, mockPurposeVersion6_2], + }; + + const mockPurpose7: Purpose = { + ...getMockPurpose(), + title: "purpose 7", + versions: [], + }; + + beforeEach(async () => { + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await addOnePurpose(mockPurpose3, postgresDB, purposes); + await addOnePurpose(mockPurpose4, postgresDB, purposes); + await addOnePurpose(mockPurpose5, postgresDB, purposes); + await addOnePurpose(mockPurpose6, postgresDB, purposes); + await addOnePurpose(mockPurpose7, postgresDB, purposes); }); - it("should get the purposes if they exist (parameters: eservicesIds)", () => { - expect(1).toBe(1); + + it("should get the purposes if they exist (parameters: name)", async () => { + const result = await purposeService.getPurposes( + { + name: "test", + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(1); + expect(result.results).toEqual([mockPurpose1]); }); - it("should get the purposes if they exist (parameters: consumersIds)", () => { - expect(1).toBe(1); + it("should get the purposes if they exist (parameters: eservicesIds)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [mockEService1ByTenant1.id], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([mockPurpose1, mockPurpose2]); }); - it("should get the purposes if they exist (parameters: producersIds)", () => { - expect(1).toBe(1); + + it("should get the purposes if they exist (parameters: consumersIds)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [consumerId1], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([mockPurpose5, mockPurpose6]); }); - it("should get the purposes if they exist (parameters: states)", () => { - expect(1).toBe(1); + it.skip("should get the purposes if they exist (parameters: producersIds)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [producerId2], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(1); + expect(result.results).toEqual([mockPurpose4]); }); - it("should not include purposes whose only version is draft (excludeDraft = true)", () => { - expect(1).toBe(1); + it("should get the purposes if they exist (parameters: states)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [ + purposeVersionState.rejected, + purposeVersionState.archived, + ], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + mockPurpose4, + mockPurpose5, + mockPurpose6, + ]); }); - it("should include purposes whose only version is draft (excludeDraft = false)", () => { - expect(1).toBe(1); + it("should not include draft versions and purposes without versions (excludeDraft = true)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: true, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(4); + expect(result.results).toEqual([ + mockPurpose3, + mockPurpose4, + mockPurpose5, + { ...mockPurpose6, versions: [mockPurposeVersion6_1] }, + ]); }); - it("should not filter out draft versions if the purpose has both draft and non-draft ones (excludeDraft = false)", () => { - expect(1).toBe(1); + it("should include draft versions and purposes without versions (excludeDraft = false)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose2, + mockPurpose3, + mockPurpose4, + mockPurpose5, + mockPurpose6, + mockPurpose7, + ]); }); - it("should filter out draft versions if the purpose has both draft and non-draft ones (excludeDraft = true)", () => { - expect(1).toBe(1); + it("should get the purposes if they exist (pagination: offset)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 5, limit: 50 } + ); + expect(result.results).toEqual([mockPurpose6, mockPurpose7]); + }); + it("should get the purposes if they exist (pagination: limit)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 3 } + ); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose2, + mockPurpose3, + ]); }); }); }); From 0ead63c4eb6b54fd1367e78b39c61ea3c80e9006 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 17:04:05 +0200 Subject: [PATCH 248/537] Fix query --- .../src/services/readModelService.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 64afb2bf87..a78cb8c7af 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -181,24 +181,24 @@ export function readModelServiceBuilder( ...draftFilter, } satisfies ReadModelFilter, }, - // producersIds.length > 0 - // ? [ - // { - // $lookup: { - // from: "eservices", - // localField: "data.eserviceId", - // foreignField: "data.id", - // as: "eservices", - // }, - // }, - // { $unwind: "$eservices" }, - // { - // $match: { - // "data.producerId": { $in: producersIds }, - // }, - // }, - // ] - // : [], + ...(producersIds.length > 0 + ? [ + { + $lookup: { + from: "eservices", + localField: "data.eserviceId", + foreignField: "data.id", + as: "eservices", + }, + }, + { $unwind: "$eservices" }, + { + $match: { + "eservices.data.producerId": { $in: producersIds }, + }, + }, + ] + : []), { $project: { data: 1, From 4619c89ed17617ea298d010fdf6194a6491f778a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 17:04:20 +0200 Subject: [PATCH 249/537] Fix test --- .../test/purposeService.integration.test.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index f0cf7e2bdd..300f522265 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -129,6 +129,7 @@ describe("database test", async () => { afterEach(async () => { await purposes.deleteMany({}); + await eservices.deleteMany({}); await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); }); @@ -1308,6 +1309,19 @@ describe("database test", async () => { await addOnePurpose(mockPurpose5, postgresDB, purposes); await addOnePurpose(mockPurpose6, postgresDB, purposes); await addOnePurpose(mockPurpose7, postgresDB, purposes); + + await writeInReadmodel( + toReadModelEService(mockEService1ByTenant1), + eservices + ); + await writeInReadmodel( + toReadModelEService(mockEService2ByTenant1), + eservices + ); + await writeInReadmodel( + toReadModelEService(mockEService3ByTenant2), + eservices + ); }); it("should get the purposes if they exist (parameters: name)", async () => { @@ -1354,7 +1368,7 @@ describe("database test", async () => { expect(result.totalCount).toBe(2); expect(result.results).toEqual([mockPurpose5, mockPurpose6]); }); - it.skip("should get the purposes if they exist (parameters: producersIds)", async () => { + it("should get the purposes if they exist (parameters: producersIds)", async () => { const result = await purposeService.getPurposes( { eservicesIds: [], From 41ec0a6690ffebdf46bf488ecbba4fac355e329b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 17:20:49 +0200 Subject: [PATCH 250/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 95877defbd..d3600bc824 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -495,7 +495,9 @@ export function purposeServiceBuilder( filters: ApiGetPurposesFilters, { offset, limit }: { offset: number; limit: number } ): Promise> { - logger.info("To DO"); + logger.info( + `Getting Purposes with name = ${filters.name}, eservicesIds = ${filters.eservicesIds}, consumers = ${filters.consumersIds}, producers = ${filters.producersIds}, states = ${filters.states}, excludeDraft = ${filters.excludeDraft}, limit = ${limit}, offset = ${offset}` + ); const purposesList = await readModelService.getPurposes( filters, From 7a186e4178a54e28c1c60a6256ecdf56ec393f8a Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 18 Apr 2024 17:42:05 +0200 Subject: [PATCH 251/537] split test for both endpoints --- .../test/purposeService.integration.test.ts | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 73d7c270c8..11dc413771 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -916,7 +916,7 @@ describe("Integration tests", async () => { }); }); - describe("updatePurpose and reverseUpdatePurpose", () => { + describe("updatePurpose and updateReversePurpose", () => { const tenantType = randomArrayItem(Object.values(tenantKind)); const tenant: Tenant = { ...getMockTenant(), @@ -947,6 +947,8 @@ describe("Integration tests", async () => { }, }; + purposeForReceive.riskAnalysisForm; + const purposeForDeliver: Purpose = { ...getMockPurpose(), eserviceId: eServiceDeliver.id, @@ -1125,7 +1127,7 @@ describe("Integration tests", async () => { }) ).rejects.toThrowError(eserviceNotFound(eserviceId)); }); - it("should throw eServiceModeNotAllowed if the eService mode is incorrect", async () => { + it("should throw eServiceModeNotAllowed if the eService mode is incorrect when expecting DELIVER", async () => { await addOnePurpose(purposeForReceive, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); await writeInReadmodel(tenant, tenants); @@ -1140,9 +1142,11 @@ describe("Integration tests", async () => { ).rejects.toThrowError( eServiceModeNotAllowed(eServiceReceive.id, "Deliver") ); - + }); + it("should throw eServiceModeNotAllowed if the eService mode is incorrect when expecting RECEIVE", async () => { await addOnePurpose(purposeForDeliver, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); expect( purposeService.updateReversePurpose({ @@ -1216,7 +1220,7 @@ describe("Integration tests", async () => { }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); - it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { + it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid in updatePurpose", async () => { await addOnePurpose(purposeForDeliver, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(tenant, tenants); @@ -1245,6 +1249,34 @@ describe("Integration tests", async () => { riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) ); }); + it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid in updateReversePurpose", async () => { + const purposeWithInvalidRiskAnalysis: Purpose = { + ...purposeForReceive, + riskAnalysisForm: { + ...purposeForReceive.riskAnalysisForm!, + version: "0", + }, + }; + + await addOnePurpose( + purposeWithInvalidRiskAnalysis, + postgresDB, + purposes + ); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeWithInvalidRiskAnalysis.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) + ); + }); }); }); }); From 8cf1f09921fd8b5b7084b77c744a5a6955c317cf Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 18 Apr 2024 17:42:47 +0200 Subject: [PATCH 252/537] deleted unused variable --- .../purpose-process/test/purposeService.integration.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 11dc413771..a84e1eb731 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -947,8 +947,6 @@ describe("Integration tests", async () => { }, }; - purposeForReceive.riskAnalysisForm; - const purposeForDeliver: Purpose = { ...getMockPurpose(), eserviceId: eServiceDeliver.id, From c7dbaa140788dd47a3fb933fb2b917fb9abda070 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 18 Apr 2024 17:46:29 +0200 Subject: [PATCH 253/537] Renaming --- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 83222ab1e6..9d28c660dc 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -374,7 +374,7 @@ export function purposeServiceBuilder( throw notValidVersionState(versionId, purposeVersion.state); } - const purposeWithoutWaiting: Purpose = { + const purposeWithoutWaitingForApproval: Purpose = { ...purpose.data, versions: purpose.data.versions.filter( (v) => v.state !== purposeVersionState.waitingForApproval @@ -386,7 +386,7 @@ export function purposeServiceBuilder( updatedAt: new Date(), }; const updatedPurpose = replacePurposeVersion( - purposeWithoutWaiting, + purposeWithoutWaitingForApproval, archivedVersion ); From f13f8e183966cdd63eecc22211265dc6618fa4a3 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 19 Apr 2024 11:36:56 +0200 Subject: [PATCH 254/537] improved tests --- .../test/purposeService.integration.test.ts | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a84e1eb731..55cf1104d9 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -1085,27 +1085,35 @@ describe("Integration tests", async () => { }) ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); }); - it("Should throw purposeNotInDraftState if the purpose is not in draft state", async () => { - const mockPurpose: Purpose = { - ...purposeForDeliver, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.active }, - ], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); - }); + it.each( + Object.values(purposeVersionState).filter( + (state) => state !== purposeVersionState.draft + ) + )( + "Should throw purposeNotInDraftState if the purpose is in state %s", + async (state) => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + versions: [{ ...getMockPurposeVersion(), state }], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel( + toReadModelEService(eServiceDeliver), + eservices + ); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); + } + ); it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { const eserviceId: EServiceId = unsafeBrandId(generateId()); const mockPurpose: Purpose = { From f1a880a7a9706d9477e29cf44f3bce6b0b58da51 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 12:11:56 +0200 Subject: [PATCH 255/537] Add tests --- .../test/purposeService.integration.test.ts | 74 ++++++++++++++++--- 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 1cd0848ed1..5d43ec288b 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -64,6 +64,7 @@ import { toPurposeV2, toReadModelEService, unsafeBrandId, + PurposeVersionState, } from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { @@ -2033,6 +2034,7 @@ describe("Integration tests", async () => { const mockPurpose1: Purpose = { ...getMockPurpose(), title: "purpose 1 - test", + consumerId: consumerId1, eserviceId: mockEService1ByTenant1.id, versions: [mockPurposeVersion1], }; @@ -2091,14 +2093,15 @@ describe("Integration tests", async () => { }; const mockPurpose6: Purpose = { ...getMockPurpose(), - title: "purpose 6", + title: "purpose 6 - test", consumerId: consumerId1, + eserviceId: mockEService3ByTenant2.id, versions: [mockPurposeVersion6_1, mockPurposeVersion6_2], }; const mockPurpose7: Purpose = { ...getMockPurpose(), - title: "purpose 7", + title: "purpose 7 - test", versions: [], }; @@ -2137,8 +2140,12 @@ describe("Integration tests", async () => { }, { offset: 0, limit: 50 } ); - expect(result.totalCount).toBe(1); - expect(result.results).toEqual([mockPurpose1]); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose6, + mockPurpose7, + ]); }); it("should get the purposes if they exist (parameters: eservicesIds)", async () => { const result = await purposeService.getPurposes( @@ -2154,7 +2161,6 @@ describe("Integration tests", async () => { expect(result.totalCount).toBe(2); expect(result.results).toEqual([mockPurpose1, mockPurpose2]); }); - it("should get the purposes if they exist (parameters: consumersIds)", async () => { const result = await purposeService.getPurposes( { @@ -2166,8 +2172,12 @@ describe("Integration tests", async () => { }, { offset: 0, limit: 50 } ); - expect(result.totalCount).toBe(2); - expect(result.results).toEqual([mockPurpose5, mockPurpose6]); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose5, + mockPurpose6, + ]); }); it("should get the purposes if they exist (parameters: producersIds)", async () => { const result = await purposeService.getPurposes( @@ -2180,8 +2190,8 @@ describe("Integration tests", async () => { }, { offset: 0, limit: 50 } ); - expect(result.totalCount).toBe(1); - expect(result.results).toEqual([mockPurpose4]); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([mockPurpose4, mockPurpose6]); }); it("should get the purposes if they exist (parameters: states)", async () => { const result = await purposeService.getPurposes( @@ -2275,6 +2285,52 @@ describe("Integration tests", async () => { mockPurpose3, ]); }); + it("should not get the purposes if they don't exist", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [generateId()], + consumersIds: [], + producersIds: [generateId()], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(0); + expect(result.results).toEqual([]); + }); + it("should get the purposes if they exist (parameters: name, eservicesIds, consumersIds, producersIds, states; exlcudeDraft = true)", async () => { + const result = await purposeService.getPurposes( + { + name: "test", + eservicesIds: [mockEService3ByTenant2.id], + consumersIds: [consumerId1], + producersIds: [producerId2], + states: [purposeVersionState.archived], + excludeDraft: true, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(1); + expect(result.results).toEqual([ + { ...mockPurpose6, versions: [mockPurposeVersion6_1] }, + ]); + }); + it("should get the purposes if they exist (parameters: name, eservicesIds, consumersIds, producersIds, states; exlcudeDraft = false)", async () => { + const result = await purposeService.getPurposes( + { + name: "test", + eservicesIds: [mockEService1ByTenant1.id], + consumersIds: [consumerId1], + producersIds: [producerId1], + states: [purposeVersionState.draft], + excludeDraft: false, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(1); + expect(result.results).toEqual([mockPurpose1]); + }); }); }); }); From 5b5e1ed0b641eab4f8437ae0272486618784e2f7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 13:53:22 +0200 Subject: [PATCH 256/537] Remove comment --- packages/purpose-process/src/routers/PurposeRouter.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 3cae914793..8e131438d0 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -207,9 +207,7 @@ const purposeRouter = ( .post( "/purposes/:purposeId/versions", authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => - // TODO - res.status(501).send() + (_req, res) => res.status(501).send() ) .delete( "/purposes/:purposeId/versions/:versionId", From 411dc0889dfa5a132617ec63d12256c8d74c9137 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 15:08:06 +0200 Subject: [PATCH 257/537] Apply suggestions --- .../src/utilities/errorMappers.ts | 1 - .../test/purposeService.integration.test.ts | 24 ------------------- 2 files changed, 25 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index d2a1408f7b..85875816a2 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -24,7 +24,6 @@ export const getRiskAnalysisDocumentErrorMapper = ( ): number => match(error.code) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) .with("purposeVersionDocumentNotFound", () => HTTP_STATUS_NOT_FOUND) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index a8348f2731..d86b74f46d 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -242,30 +242,6 @@ describe("Integration tests", async () => { }) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); - it("should throw eserviceNotFound if the eservice doesn't exist", async () => { - const mockDocument = getMockPurposeVersionDocument(); - const mockEService = getMockEService(); - const mockPurposeVersion = { - ...getMockPurposeVersion(), - riskAnalysis: mockDocument, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - - expect( - purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - documentId: mockDocument.id, - organizationId: mockEService.producerId, - }) - ).rejects.toThrowError(eserviceNotFound(mockEService.id)); - }); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const randomVersionId: PurposeVersionId = generateId(); const randomDocumentId: PurposeVersionDocumentId = generateId(); From 51a51ce1f6b70769711a2d6a5cd3922e5fa4a525 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:18:08 +0200 Subject: [PATCH 258/537] Split test files --- .../test/purposeService.integration.test.ts | 107 ++------------- .../test/testGetPurposeById.ts | 124 ++++++++++++++++++ 2 files changed, 133 insertions(+), 98 deletions(-) create mode 100644 packages/purpose-process/test/testGetPurposeById.ts diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 5bcdf0e918..fe6d66e8e5 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -48,14 +48,16 @@ import { tenantNotFound, } from "../src/model/domain/errors.js"; import { addOnePurpose, getMockEService } from "./utils.js"; +import { testGetPurposeById } from "./testGetPurposeById.js"; + +export let purposes: PurposeCollection; +export let eservices: EServiceCollection; +export let tenants: TenantCollection; +export let readModelService: ReadModelService; +export let purposeService: PurposeService; +export let postgresDB: IDatabase; describe("Integration tests", async () => { - let purposes: PurposeCollection; - let eservices: EServiceCollection; - let tenants: TenantCollection; - let readModelService: ReadModelService; - let purposeService: PurposeService; - let postgresDB: IDatabase; let startedPostgreSqlContainer: StartedTestContainer; let startedMongodbContainer: StartedTestContainer; @@ -99,97 +101,6 @@ describe("Integration tests", async () => { }); describe("Purpose service", () => { - describe("getPurposeById", () => { - it("should get the purpose if it exists", async () => { - const mockTenant = { - ...getMockTenant(), - kind: tenantKind.PA, - }; - - const mockEService = getMockEService(); - const mockPurpose1: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(mockTenant, tenants); - - const result = await purposeService.getPurposeById( - mockPurpose1.id, - mockTenant.id - ); - expect(result).toMatchObject({ - purpose: mockPurpose1, - isRiskAnalysisValid: false, - }); - }); - it("should throw purposeNotFound if the purpose doesn't exist", async () => { - const notExistingId: PurposeId = generateId(); - const mockTenant = getMockTenant(); - const mockPurpose = getMockPurpose(); - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(mockTenant, tenants); - - expect( - purposeService.getPurposeById(notExistingId, mockTenant.id) - ).rejects.toThrowError(purposeNotFound(notExistingId)); - }); - it("should throw eserviceNotFound if the eservice doesn't exist", async () => { - const notExistingId: EServiceId = generateId(); - const mockTenant = { - ...getMockTenant(), - kind: tenantKind.PA, - }; - - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: notExistingId, - }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(mockTenant, tenants); - - expect( - purposeService.getPurposeById(mockPurpose.id, mockTenant.id) - ).rejects.toThrowError(eserviceNotFound(notExistingId)); - }); - it("should throw tenantNotFound if the tenant doesn't exist", async () => { - const notExistingId: TenantId = generateId(); - const mockEService = getMockEService(); - - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.getPurposeById(mockPurpose.id, notExistingId) - ).rejects.toThrowError(tenantNotFound(notExistingId)); - }); - it("should throw tenantKindNotFound if the tenant doesn't exist", async () => { - const mockTenant = getMockTenant(); - const mockEService = getMockEService(); - - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(mockTenant, tenants); - - expect( - purposeService.getPurposeById(mockPurpose.id, mockTenant.id) - ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); - }); - }); + testGetPurposeById(); }); }); diff --git a/packages/purpose-process/test/testGetPurposeById.ts b/packages/purpose-process/test/testGetPurposeById.ts new file mode 100644 index 0000000000..48c5dfac2e --- /dev/null +++ b/packages/purpose-process/test/testGetPurposeById.ts @@ -0,0 +1,124 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + getMockTenant, + getMockPurpose, + writeInReadmodel, +} from "pagopa-interop-commons-test/index.js"; +import { + tenantKind, + Purpose, + generateId, + toReadModelEService, + PurposeId, + EServiceId, + TenantId, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { + purposeNotFound, + eserviceNotFound, + tenantNotFound, + tenantKindNotFound, +} from "../src/model/domain/errors.js"; +import { getMockEService, addOnePurpose } from "./utils.js"; +import { + eservices, + postgresDB, + purposeService, + purposes, + tenants, +} from "./purposeService.integration.test.js"; + +export const testGetPurposeById = (): ReturnType => + describe("getPurposeById", () => { + it("should get the purpose if it exists", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockEService = getMockEService(); + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(mockTenant, tenants); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + mockTenant.id + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: false, + }); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const notExistingId: PurposeId = generateId(); + const mockTenant = getMockTenant(); + const mockPurpose = getMockPurpose(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.getPurposeById(notExistingId, mockTenant.id) + ).rejects.toThrowError(purposeNotFound(notExistingId)); + }); + it("should throw eserviceNotFound if the eservice doesn't exist", async () => { + const notExistingId: EServiceId = generateId(); + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: notExistingId, + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.getPurposeById(mockPurpose.id, mockTenant.id) + ).rejects.toThrowError(eserviceNotFound(notExistingId)); + }); + it("should throw tenantNotFound if the tenant doesn't exist", async () => { + const notExistingId: TenantId = generateId(); + const mockEService = getMockEService(); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getPurposeById(mockPurpose.id, notExistingId) + ).rejects.toThrowError(tenantNotFound(notExistingId)); + }); + it("should throw tenantKindNotFound if the tenant doesn't exist", async () => { + const mockTenant = getMockTenant(); + const mockEService = getMockEService(); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.getPurposeById(mockPurpose.id, mockTenant.id) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + }); From 00f2205b6cce85788b0b5f26b3659014c16b4976 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:20:20 +0200 Subject: [PATCH 259/537] Split test file --- .../test/testGetRiskAnalysisDocument.ts | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 packages/purpose-process/test/testGetRiskAnalysisDocument.ts diff --git a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts new file mode 100644 index 0000000000..2e1b2d84b7 --- /dev/null +++ b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts @@ -0,0 +1,162 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + getMockPurposeVersionDocument, + getMockPurposeVersion, + getMockPurpose, + writeInReadmodel, +} from "pagopa-interop-commons-test/index.js"; +import { + Purpose, + generateId, + toReadModelEService, + PurposeId, + PurposeVersionId, + PurposeVersionDocumentId, + TenantId, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { + purposeNotFound, + purposeVersionNotFound, + purposeVersionDocumentNotFound, + organizationNotAllowed, +} from "../src/model/domain/errors.js"; +import { + postgresDB, + purposes, + eservices, + purposeService, +} from "./purposeService.integration.test.js"; +import { getMockEService, addOnePurpose } from "./utils.js"; + +describe("getRiskAnalysisDocument", () => { + it("should get the purpose version document", async () => { + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const result = await purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: mockEService.producerId, + }); + expect(result).toEqual(mockDocument); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const notExistingId: PurposeId = generateId(); + await addOnePurpose(getMockPurpose(), postgresDB, purposes); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: notExistingId, + versionId: generateId(), + documentId: generateId(), + organizationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(notExistingId)); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const randomVersionId: PurposeVersionId = generateId(); + const randomDocumentId: PurposeVersionDocumentId = generateId(); + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: randomVersionId, + documentId: randomDocumentId, + organizationId: mockEService.producerId, + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose.id, randomVersionId) + ); + }); + it("should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { + const mockDocument = getMockPurposeVersionDocument(); + const randomDocumentId: PurposeVersionDocumentId = generateId(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: randomDocumentId, + organizationId: mockEService.producerId, + }) + ).rejects.toThrowError( + purposeVersionDocumentNotFound( + mockPurpose.id, + mockPurposeVersion.id, + randomDocumentId + ) + ); + }); + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { + const randomId: TenantId = generateId(); + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: randomId, + }) + ).rejects.toThrowError(organizationNotAllowed(randomId)); + }); +}); From bae33ae70922c776f4edf3026de47a1e41046397 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:23:54 +0200 Subject: [PATCH 260/537] Split test file --- .../test/testDeletePurposeVersion.ts | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 packages/purpose-process/test/testDeletePurposeVersion.ts diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts new file mode 100644 index 0000000000..63435ca2ce --- /dev/null +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -0,0 +1,263 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { describe, expect, it, vi } from "vitest"; +import { + getMockPurposeVersion, + getMockPurpose, + writeInReadmodel, + readLastEventByStreamId, + decodeProtobufPayload, +} from "pagopa-interop-commons-test/index.js"; +import { + purposeVersionState, + Purpose, + toReadModelEService, + generateId, + WaitingForApprovalPurposeVersionDeletedV2, + toPurposeV2, + PurposeId, + PurposeVersionId, + PurposeVersion, +} from "pagopa-interop-models"; +import { + purposeNotFound, + purposeVersionNotFound, + organizationIsNotTheConsumer, + purposeVersionCannotBeDeleted, +} from "../src/model/domain/errors.js"; +import { addOnePurpose, getMockEService } from "./utils.js"; +import { + postgresDB, + purposes, + eservices, + purposeService, +} from "./purposeService.integration.test.js"; + +export const testDeletePurposeVersion = (): ReturnType => + describe("deletePurposeVersion", () => { + it("should write in event-store for the deletion of a purpose version", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "WaitingForApprovalPurposeVersionDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: WaitingForApprovalPurposeVersionDeletedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [], + updatedAt: new Date(), + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomId: PurposeId = generateId(); + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: randomId, + versionId: mockPurposeVersion.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomId)); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const randomVersionId: PurposeVersionId = generateId(); + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [getMockPurposeVersion()], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: randomVersionId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose.id, randomVersionId) + ); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheConsumer(mockEService.producerId) + ); + }); + it("should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + }); + it("should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + }); + it("should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + }); + it("should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.suspended, + suspendedAt: new Date(), + }; + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + }); + }); From 86db9562d64ab42cfff0264109b386b10342144d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:24:43 +0200 Subject: [PATCH 261/537] Fix --- .../test/purposeService.integration.test.ts | 2 + .../test/testGetRiskAnalysisDocument.ts | 243 +++++++++--------- 2 files changed, 124 insertions(+), 121 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 4f87025ca3..cf7b39c038 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -56,6 +56,7 @@ import { } from "../src/model/domain/errors.js"; import { addOnePurpose, getMockEService } from "./utils.js"; import { testGetPurposeById } from "./testGetPurposeById.js"; +import { testGetRiskAnalysisDocument } from "./testGetRiskAnalysisDocument.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -109,5 +110,6 @@ describe("Integration tests", async () => { describe("Purpose service", () => { testGetPurposeById(); + testGetRiskAnalysisDocument(); }); }); diff --git a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts index 2e1b2d84b7..bf2c754105 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts @@ -29,134 +29,135 @@ import { } from "./purposeService.integration.test.js"; import { getMockEService, addOnePurpose } from "./utils.js"; -describe("getRiskAnalysisDocument", () => { - it("should get the purpose version document", async () => { - const mockDocument = getMockPurposeVersionDocument(); - const mockEService = getMockEService(); - const mockPurposeVersion = { - ...getMockPurposeVersion(), - riskAnalysis: mockDocument, - }; - const mockPurpose1: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; - await addOnePurpose(mockPurpose1, postgresDB, purposes); - await addOnePurpose(mockPurpose2, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); +export const testGetRiskAnalysisDocument = (): ReturnType => + describe("getRiskAnalysisDocument", () => { + it("should get the purpose version document", async () => { + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + id: generateId(), + title: "another purpose", + }; + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - const result = await purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose1.id, - versionId: mockPurposeVersion.id, - documentId: mockDocument.id, - organizationId: mockEService.producerId, + const result = await purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose1.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: mockEService.producerId, + }); + expect(result).toEqual(mockDocument); }); - expect(result).toEqual(mockDocument); - }); - it("should throw purposeNotFound if the purpose doesn't exist", async () => { - const notExistingId: PurposeId = generateId(); - await addOnePurpose(getMockPurpose(), postgresDB, purposes); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const notExistingId: PurposeId = generateId(); + await addOnePurpose(getMockPurpose(), postgresDB, purposes); - expect( - purposeService.getRiskAnalysisDocument({ - purposeId: notExistingId, - versionId: generateId(), - documentId: generateId(), - organizationId: generateId(), - }) - ).rejects.toThrowError(purposeNotFound(notExistingId)); - }); - it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { - const randomVersionId: PurposeVersionId = generateId(); - const randomDocumentId: PurposeVersionDocumentId = generateId(); - const mockDocument = getMockPurposeVersionDocument(); - const mockEService = getMockEService(); - const mockPurposeVersion = { - ...getMockPurposeVersion(), - riskAnalysis: mockDocument, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: notExistingId, + versionId: generateId(), + documentId: generateId(), + organizationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(notExistingId)); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const randomVersionId: PurposeVersionId = generateId(); + const randomDocumentId: PurposeVersionDocumentId = generateId(); + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect( - purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose.id, - versionId: randomVersionId, - documentId: randomDocumentId, - organizationId: mockEService.producerId, - }) - ).rejects.toThrowError( - purposeVersionNotFound(mockPurpose.id, randomVersionId) - ); - }); - it("should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { - const mockDocument = getMockPurposeVersionDocument(); - const randomDocumentId: PurposeVersionDocumentId = generateId(); - const mockEService = getMockEService(); - const mockPurposeVersion = { - ...getMockPurposeVersion(), - riskAnalysis: mockDocument, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: randomVersionId, + documentId: randomDocumentId, + organizationId: mockEService.producerId, + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose.id, randomVersionId) + ); + }); + it("should throw purposeVersionDocumentNotFound if the document doesn't exist", async () => { + const mockDocument = getMockPurposeVersionDocument(); + const randomDocumentId: PurposeVersionDocumentId = generateId(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect( - purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - documentId: randomDocumentId, - organizationId: mockEService.producerId, - }) - ).rejects.toThrowError( - purposeVersionDocumentNotFound( - mockPurpose.id, - mockPurposeVersion.id, - randomDocumentId - ) - ); - }); - it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { - const randomId: TenantId = generateId(); - const mockDocument = getMockPurposeVersionDocument(); - const mockEService = getMockEService(); - const mockPurposeVersion = { - ...getMockPurposeVersion(), - riskAnalysis: mockDocument, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: randomDocumentId, + organizationId: mockEService.producerId, + }) + ).rejects.toThrowError( + purposeVersionDocumentNotFound( + mockPurpose.id, + mockPurposeVersion.id, + randomDocumentId + ) + ); + }); + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { + const randomId: TenantId = generateId(); + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect( - purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - documentId: mockDocument.id, - organizationId: randomId, - }) - ).rejects.toThrowError(organizationNotAllowed(randomId)); + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: randomId, + }) + ).rejects.toThrowError(organizationNotAllowed(randomId)); + }); }); -}); From 2ea015146cb8f5063959bdce1d6a6f6e7a5c4d43 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:26:22 +0200 Subject: [PATCH 262/537] Remove unused import --- .../test/purposeService.integration.test.ts | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index fe6d66e8e5..8b8fca8fcc 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -12,26 +12,13 @@ import { initDB, } from "pagopa-interop-commons"; import { IDatabase } from "pg-promise"; - import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, - getMockPurpose, - getMockTenant, mongoDBContainer, postgreSQLContainer, - writeInReadmodel, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; -import { - EServiceId, - Purpose, - PurposeId, - TenantId, - generateId, - tenantKind, - toReadModelEService, -} from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { PurposeService, @@ -41,13 +28,6 @@ import { ReadModelService, readModelServiceBuilder, } from "../src/services/readModelService.js"; -import { - eserviceNotFound, - purposeNotFound, - tenantKindNotFound, - tenantNotFound, -} from "../src/model/domain/errors.js"; -import { addOnePurpose, getMockEService } from "./utils.js"; import { testGetPurposeById } from "./testGetPurposeById.js"; export let purposes: PurposeCollection; From 5477b0f1961152ca266b399c5692ab859f974aba Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:26:40 +0200 Subject: [PATCH 263/537] Remove unused import --- .../test/purposeService.integration.test.ts | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index cf7b39c038..e5dd31434b 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -3,7 +3,7 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest"; +import { afterAll, afterEach, beforeAll, describe } from "vitest"; import { EServiceCollection, PurposeCollection, @@ -16,26 +16,10 @@ import { IDatabase } from "pg-promise"; import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, - getMockPurpose, - getMockPurposeVersion, - getMockPurposeVersionDocument, - getMockTenant, mongoDBContainer, postgreSQLContainer, - writeInReadmodel, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; -import { - EServiceId, - Purpose, - PurposeId, - PurposeVersionDocumentId, - PurposeVersionId, - TenantId, - generateId, - tenantKind, - toReadModelEService, -} from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { PurposeService, @@ -45,16 +29,6 @@ import { ReadModelService, readModelServiceBuilder, } from "../src/services/readModelService.js"; -import { - eserviceNotFound, - organizationNotAllowed, - purposeNotFound, - purposeVersionDocumentNotFound, - purposeVersionNotFound, - tenantKindNotFound, - tenantNotFound, -} from "../src/model/domain/errors.js"; -import { addOnePurpose, getMockEService } from "./utils.js"; import { testGetPurposeById } from "./testGetPurposeById.js"; import { testGetRiskAnalysisDocument } from "./testGetRiskAnalysisDocument.js"; From 97d8d53bbf39511246fe68249b5b33efc86fff52 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:27:01 +0200 Subject: [PATCH 264/537] Delete unused imports --- .../test/purposeService.integration.test.ts | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index cbd109ac89..9aa3a06a4a 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -3,15 +3,7 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { - afterAll, - afterEach, - beforeAll, - describe, - expect, - it, - vi, -} from "vitest"; +import { afterAll, afterEach, beforeAll, describe } from "vitest"; import { EServiceCollection, PurposeCollection, @@ -24,32 +16,10 @@ import { IDatabase } from "pg-promise"; import { TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, - decodeProtobufPayload, - getMockPurpose, - getMockPurposeVersion, - getMockPurposeVersionDocument, - getMockTenant, mongoDBContainer, postgreSQLContainer, - readLastEventByStreamId, - writeInReadmodel, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; -import { - EServiceId, - Purpose, - PurposeId, - PurposeVersion, - PurposeVersionDocumentId, - PurposeVersionId, - TenantId, - WaitingForApprovalPurposeVersionDeletedV2, - generateId, - purposeVersionState, - tenantKind, - toPurposeV2, - toReadModelEService, -} from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { PurposeService, @@ -59,18 +29,6 @@ import { ReadModelService, readModelServiceBuilder, } from "../src/services/readModelService.js"; -import { - eserviceNotFound, - organizationIsNotTheConsumer, - organizationNotAllowed, - purposeNotFound, - purposeVersionCannotBeDeleted, - purposeVersionDocumentNotFound, - purposeVersionNotFound, - tenantKindNotFound, - tenantNotFound, -} from "../src/model/domain/errors.js"; -import { addOnePurpose, getMockEService } from "./utils.js"; import { testGetPurposeById } from "./testGetPurposeById.js"; import { testGetRiskAnalysisDocument } from "./testGetRiskAnalysisDocument.js"; import { testDeletePurposeVersion } from "./testDeletePurposeVersion.js"; From eba5b8aa16105cf9b7527bc84b60d26aee7425d5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:33:27 +0200 Subject: [PATCH 265/537] Improve test --- packages/commons-test/src/testUtils.ts | 13 +- .../test/testDeletePurposeVersion.ts | 137 ++++-------------- 2 files changed, 43 insertions(+), 107 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 9727a15e8b..39df7e3754 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -23,6 +23,8 @@ import { generateId, tenantAttributeType, PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, } from "pagopa-interop-models"; import { v4 as uuidv4 } from "uuid"; import { AuthData } from "pagopa-interop-commons"; @@ -160,9 +162,11 @@ export const getMockPurpose = (): Purpose => ({ isFreeOfCharge: true, }); -export const getMockPurposeVersion = (): PurposeVersion => ({ +export const getMockPurposeVersion = ( + state?: PurposeVersionState +): PurposeVersion => ({ id: generateId(), - state: "Draft", + state: state || purposeVersionState.draft, riskAnalysis: { id: generateId(), contentType: "json", @@ -171,6 +175,11 @@ export const getMockPurposeVersion = (): PurposeVersion => ({ }, dailyCalls: 10, createdAt: new Date(), + updatedAt: state !== purposeVersionState.draft ? new Date() : undefined, + firstActivationAt: + state !== purposeVersionState.draft ? new Date() : undefined, + suspendedAt: state === purposeVersionState.suspended ? new Date() : undefined, + rejectionReason: state === purposeVersionState.rejected ? "test" : undefined, }); export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index 63435ca2ce..c8c585e301 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -155,109 +155,36 @@ export const testDeletePurposeVersion = (): ReturnType => organizationIsNotTheConsumer(mockEService.producerId) ); }); - it("should throw purposeVersionCannotBeDeleted if the purpose version is in draft state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }; - const mockEService = getMockEService(); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) - ); - }); - it("should throw purposeVersionCannotBeDeleted if the purpose version is in active state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.active, - }; - const mockEService = getMockEService(); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) - ); - }); - it("should throw purposeVersionCannotBeDeleted if the purpose version is in archived state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.archived, - }; - const mockEService = getMockEService(); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) - ); - }); - it("should throw purposeVersionCannotBeDeleted if the purpose version is in suspended state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.suspended, - suspendedAt: new Date(), - }; - const mockEService = getMockEService(); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) - ); - }); + it.each( + Object.values(purposeVersionState).filter( + (state) => state !== purposeVersionState.waitingForApproval + ) + )( + "should throw purposeVersionCannotBeDeleted if the purpose version is in %s state", + async (state) => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(state), + }; + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + } + ); }); From 7f57bb519877fcf9f5b9cd0c1f7badf22a4a6e3c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:35:54 +0200 Subject: [PATCH 266/537] Split test files --- .../test/testRejectPurposeVersion.ts | 327 ++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 packages/purpose-process/test/testRejectPurposeVersion.ts diff --git a/packages/purpose-process/test/testRejectPurposeVersion.ts b/packages/purpose-process/test/testRejectPurposeVersion.ts new file mode 100644 index 0000000000..f421213abe --- /dev/null +++ b/packages/purpose-process/test/testRejectPurposeVersion.ts @@ -0,0 +1,327 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + getMockPurposeVersion, + getMockPurpose, + writeInReadmodel, + readLastEventByStreamId, + decodeProtobufPayload, +} from "pagopa-interop-commons-test/index.js"; +import { + purposeVersionState, + Purpose, + toReadModelEService, + generateId, + PurposeVersionRejectedV2, + PurposeVersion, + toPurposeV2, + PurposeId, + PurposeVersionId, +} from "pagopa-interop-models"; +import { describe, expect, it, vi } from "vitest"; +import { + purposeNotFound, + eserviceNotFound, + organizationIsNotTheProducer, + purposeVersionNotFound, + notValidVersionState, +} from "../src/model/domain/errors.js"; +import { + postgresDB, + purposes, + eservices, + purposeService, +} from "./purposeService.integration.test.js"; +import { getMockEService, addOnePurpose } from "./utils.js"; + +export const testRejectPurposeVersion = (): ReturnType => + describe("rejectPurposeVersion", () => { + it("should write on event-store for the rejection of a purpose version", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeVersionRejected", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionRejectedV2, + payload: writtenEvent.data, + }); + + const expectedPurposeVersion: PurposeVersion = { + ...mockPurposeVersion, + state: purposeVersionState.rejected, + rejectionReason: "test", + updatedAt: new Date(), + }; + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [expectedPurposeVersion], + updatedAt: new Date(), + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const randomId: PurposeId = generateId(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: randomId, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomId)); + }); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(eserviceNotFound(mockEService.id)); + }); + it("should throw organizationIsNotTheProducer if the requester is not the producer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheProducer(mockPurpose.consumerId) + ); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: randomVersionId, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose.id, randomVersionId) + ); + }); + it("should throw notValidVersionState if the purpose version is in draft state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in active state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in archived state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in rejected state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in suspended state", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.suspended, + suspendedAt: new Date(), + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + }); From 17dc5d7f537356743fad53a777dd75e47773d5a6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:38:03 +0200 Subject: [PATCH 267/537] Split test file --- .../test/testRejectPurposeVersion.ts | 170 ++++-------------- 1 file changed, 34 insertions(+), 136 deletions(-) diff --git a/packages/purpose-process/test/testRejectPurposeVersion.ts b/packages/purpose-process/test/testRejectPurposeVersion.ts index f421213abe..748e1d9d58 100644 --- a/packages/purpose-process/test/testRejectPurposeVersion.ts +++ b/packages/purpose-process/test/testRejectPurposeVersion.ts @@ -188,140 +188,38 @@ export const testRejectPurposeVersion = (): ReturnType => purposeVersionNotFound(mockPurpose.id, randomVersionId) ); }); - it("should throw notValidVersionState if the purpose version is in draft state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); - it("should throw notValidVersionState if the purpose version is in active state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.active, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); - it("should throw notValidVersionState if the purpose version is in archived state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.archived, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); - it("should throw notValidVersionState if the purpose version is in rejected state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.rejected, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); - it("should throw notValidVersionState if the purpose version is in suspended state", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.suspended, - suspendedAt: new Date(), - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.rejectPurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - rejectionReason: "test", - organizationId: mockEService.producerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); + it.each( + Object.values(purposeVersionState).filter( + (state) => state !== purposeVersionState.waitingForApproval + ) + )( + "should throw notValidVersionState if the purpose version is in %s state", + async (state) => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(state), + state: purposeVersionState.draft, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + } + ); }); From d102f3181022117c3b2017179321680eb0d648ab Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 17:48:48 +0200 Subject: [PATCH 268/537] Fix --- packages/models/src/purpose/purposeEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/purposeEvents.ts b/packages/models/src/purpose/purposeEvents.ts index bfb0a6c0b8..d7778608c8 100644 --- a/packages/models/src/purpose/purposeEvents.ts +++ b/packages/models/src/purpose/purposeEvents.ts @@ -42,7 +42,7 @@ export function purposeEventToBinaryData(event: PurposeEvent): Uint8Array { .exhaustive(); } -export function purposeEventToBinaryDataV1(event: PurposeEvent): Uint8Array { +export function purposeEventToBinaryDataV1(event: PurposeEventV1): Uint8Array { return match(event) .with({ type: "PurposeCreated" }, ({ data }) => PurposeCreatedV1.toBinary(data) From 65dd8d03da23d25457b8ee2adcd0fc2932e53233 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 18:02:42 +0200 Subject: [PATCH 269/537] Fix --- packages/commons-test/src/testUtils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 39df7e3754..8548021fb2 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -175,10 +175,12 @@ export const getMockPurposeVersion = ( }, dailyCalls: 10, createdAt: new Date(), - updatedAt: state !== purposeVersionState.draft ? new Date() : undefined, + ...(state !== purposeVersionState.draft ? { updatedAt: new Date() } : {}), firstActivationAt: state !== purposeVersionState.draft ? new Date() : undefined, - suspendedAt: state === purposeVersionState.suspended ? new Date() : undefined, + ...(state === purposeVersionState.suspended + ? { suspendedAt: new Date() } + : {}), rejectionReason: state === purposeVersionState.rejected ? "test" : undefined, }); From 021e42789b4ccde28c11cd09cbb438815c542d5d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 18:06:41 +0200 Subject: [PATCH 270/537] Fix --- packages/commons-test/src/testUtils.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 8548021fb2..3e150468b5 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -175,13 +175,15 @@ export const getMockPurposeVersion = ( }, dailyCalls: 10, createdAt: new Date(), - ...(state !== purposeVersionState.draft ? { updatedAt: new Date() } : {}), - firstActivationAt: - state !== purposeVersionState.draft ? new Date() : undefined, + ...(state !== purposeVersionState.draft + ? { updatedAt: new Date(), firstActivationAt: new Date() } + : {}), ...(state === purposeVersionState.suspended ? { suspendedAt: new Date() } : {}), - rejectionReason: state === purposeVersionState.rejected ? "test" : undefined, + ...(state === purposeVersionState.rejected + ? { rejectionReason: "test" } + : {}), }); export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ From 4efb356476375cd26f72ed29718489ca1969b151 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 18:09:40 +0200 Subject: [PATCH 271/537] Fix --- packages/commons-test/src/testUtils.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 8548021fb2..3e150468b5 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -175,13 +175,15 @@ export const getMockPurposeVersion = ( }, dailyCalls: 10, createdAt: new Date(), - ...(state !== purposeVersionState.draft ? { updatedAt: new Date() } : {}), - firstActivationAt: - state !== purposeVersionState.draft ? new Date() : undefined, + ...(state !== purposeVersionState.draft + ? { updatedAt: new Date(), firstActivationAt: new Date() } + : {}), ...(state === purposeVersionState.suspended ? { suspendedAt: new Date() } : {}), - rejectionReason: state === purposeVersionState.rejected ? "test" : undefined, + ...(state === purposeVersionState.rejected + ? { rejectionReason: "test" } + : {}), }); export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ From 141c7fababf50ddb04c0978a45d02e5e586ce93f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 18:10:56 +0200 Subject: [PATCH 272/537] Minor refactor --- packages/purpose-process/test/testDeletePurposeVersion.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index c8c585e301..a08c5090f9 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -162,14 +162,12 @@ export const testDeletePurposeVersion = (): ReturnType => )( "should throw purposeVersionCannotBeDeleted if the purpose version is in %s state", async (state) => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(state), - }; + const mockPurposeVersion = getMockPurposeVersion(state); const mockEService = getMockEService(); const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [getMockPurposeVersion(state)], }; await addOnePurpose(mockPurpose, postgresDB, purposes); From 25f85474c52692006828208c2317b505400fc361 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 18:12:32 +0200 Subject: [PATCH 273/537] Fix and minor refactor --- packages/purpose-process/test/testRejectPurposeVersion.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/testRejectPurposeVersion.ts b/packages/purpose-process/test/testRejectPurposeVersion.ts index 748e1d9d58..3b01b09c65 100644 --- a/packages/purpose-process/test/testRejectPurposeVersion.ts +++ b/packages/purpose-process/test/testRejectPurposeVersion.ts @@ -196,10 +196,8 @@ export const testRejectPurposeVersion = (): ReturnType => "should throw notValidVersionState if the purpose version is in %s state", async (state) => { const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(state), - state: purposeVersionState.draft, - }; + const mockPurposeVersion = getMockPurposeVersion(state); + const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, From f5509e3c6a89f37bbe02ac5be621ea04cdd0e0bc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 19 Apr 2024 18:17:58 +0200 Subject: [PATCH 274/537] Fix --- packages/purpose-process/test/testDeletePurposeVersion.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index a08c5090f9..eaab7b6663 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -16,7 +16,6 @@ import { toPurposeV2, PurposeId, PurposeVersionId, - PurposeVersion, } from "pagopa-interop-models"; import { purposeNotFound, @@ -167,7 +166,7 @@ export const testDeletePurposeVersion = (): ReturnType => const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, - versions: [getMockPurposeVersion(state)], + versions: [mockPurposeVersion], }; await addOnePurpose(mockPurpose, postgresDB, purposes); From 52aea7df964f8d28a4e00e1d8254a63bd959dec3 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 19 Apr 2024 18:21:39 +0200 Subject: [PATCH 275/537] Split tests --- .../test/purposeService.integration.test.ts | 373 +--------------- .../purpose-process/test/testUpdatePurpose.ts | 422 ++++++++++++++++++ 2 files changed, 424 insertions(+), 371 deletions(-) create mode 100644 packages/purpose-process/test/testUpdatePurpose.ts diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index f9435df71f..2738593e13 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -10,7 +10,6 @@ import { ReadModelRepository, TenantCollection, initDB, - unexpectedRulesVersionError, } from "pagopa-interop-commons"; import { IDatabase } from "pg-promise"; import { @@ -33,6 +32,7 @@ import { testGetPurposeById } from "./testGetPurposeById.js"; import { testGetRiskAnalysisDocument } from "./testGetRiskAnalysisDocument.js"; import { testDeletePurposeVersion } from "./testDeletePurposeVersion.js"; import { testRejectPurposeVersion } from "./testRejectPurposeVersion.js"; +import { testUpdatePurpose } from "./testUpdatePurpose.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -89,375 +89,6 @@ describe("Integration tests", async () => { testGetRiskAnalysisDocument(); testDeletePurposeVersion(); testRejectPurposeVersion(); - - describe("updatePurpose and updateReversePurpose", () => { - const tenantType = randomArrayItem(Object.values(tenantKind)); - const tenant: Tenant = { - ...getMockTenant(), - kind: tenantType, - }; - - const eServiceDeliver: EService = { - ...getMockEService(), - mode: "Deliver", - }; - - const eServiceReceive: EService = { - ...getMockEService(), - mode: "Receive", - producerId: tenant.id, - }; - - const purposeForReceive: Purpose = { - ...getMockPurpose(), - eserviceId: eServiceReceive.id, - consumerId: tenant.id, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.draft }, - ], - riskAnalysisForm: { - ...getMockValidRiskAnalysisForm(tenantType), - id: generateId(), - }, - }; - - const purposeForDeliver: Purpose = { - ...getMockPurpose(), - eserviceId: eServiceDeliver.id, - consumerId: tenant.id, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.draft }, - ], - }; - - const validRiskAnalysis = getMockValidRiskAnalysis(tenantType); - - const purposeUpdateContent: ApiPurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: true, - freeOfChargeReason: "reason", - riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), - }; - - const reversePurposeUpdateContent: ApiReversePurposeUpdateContent = { - ...purposeUpdateContent, - }; - - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - await purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }); - - const writtenEvent = await readLastEventByStreamId( - purposeForDeliver.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: purposeForDeliver.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, - }); - - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, - }); - - const expectedPurpose: Purpose = createUpdatedPurpose( - purposeForDeliver, - purposeUpdateContent, - validRiskAnalysis, - writtenPayload - ); - - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - }); - it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { - await addOnePurpose(purposeForReceive, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); - await writeInReadmodel(tenant, tenants); - - await purposeService.updateReversePurpose({ - purposeId: purposeForReceive.id, - reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }); - - const writtenEvent = await readLastEventByStreamId( - purposeForReceive.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: purposeForReceive.id, - version: "1", - type: "DraftPurposeUpdated", - event_version: 2, - }); - - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeUpdatedV2, - payload: writtenEvent.data, - }); - - const expectedPurpose: Purpose = createUpdatedPurpose( - purposeForReceive, - reversePurposeUpdateContent, - validRiskAnalysis, - writtenPayload - ); - - expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - }); - it("Should throw purposeNotFound if the purpose doesn't exist", async () => { - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - const purposeId: PurposeId = unsafeBrandId(generateId()); - - expect( - purposeService.updatePurpose({ - purposeId, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(purposeNotFound(purposeId)); - }); - it("Should throw organizationIsNotTheConsumer if the organization is not the consumer", async () => { - const mockPurpose: Purpose = { - ...purposeForDeliver, - consumerId: generateId(), - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - const organizationId: TenantId = unsafeBrandId(generateId()); - - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId, - correlationId: generateId(), - }) - ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); - }); - it.each( - Object.values(purposeVersionState).filter( - (state) => state !== purposeVersionState.draft - ) - )( - "Should throw purposeNotInDraftState if the purpose is in state %s", - async (state) => { - const mockPurpose: Purpose = { - ...purposeForDeliver, - versions: [{ ...getMockPurposeVersion(), state }], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel( - toReadModelEService(eServiceDeliver), - eservices - ); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); - } - ); - it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { - const eserviceId: EServiceId = unsafeBrandId(generateId()); - const mockPurpose: Purpose = { - ...purposeForDeliver, - eserviceId, - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updatePurpose({ - purposeId: mockPurpose.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(eserviceNotFound(eserviceId)); - }); - it("should throw eServiceModeNotAllowed if the eService mode is incorrect when expecting DELIVER", async () => { - await addOnePurpose(purposeForReceive, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updatePurpose({ - purposeId: purposeForReceive.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError( - eServiceModeNotAllowed(eServiceReceive.id, "Deliver") - ); - }); - it("should throw eServiceModeNotAllowed if the eService mode is incorrect when expecting RECEIVE", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updateReversePurpose({ - purposeId: purposeForDeliver.id, - reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError( - eServiceModeNotAllowed(eServiceDeliver.id, "Receive") - ); - }); - it("Should throw missingFreeOfChargeReason if the freeOfChargeReason is missing", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: { - ...purposeUpdateContent, - freeOfChargeReason: undefined, - }, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(missingFreeOfChargeReason()); - }); - it("Should throw tenantNotFound if the tenant does not exist", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(tenantNotFound(tenant.id)); - - await addOnePurpose(purposeForReceive, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); - - expect( - purposeService.updateReversePurpose({ - purposeId: purposeForReceive.id, - reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(tenantNotFound(tenant.id)); - }); - it("Should throw tenantKindNotFound if the tenant kind does not exist", async () => { - const mockTenant = { - ...tenant, - kind: undefined, - }; - - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(mockTenant, tenants); - - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent, - organizationId: mockTenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); - }); - it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid in updatePurpose", async () => { - await addOnePurpose(purposeForDeliver, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); - await writeInReadmodel(tenant, tenants); - - const invalidRiskAnalysis: RiskAnalysis = { - ...validRiskAnalysis, - riskAnalysisForm: { - ...validRiskAnalysis.riskAnalysisForm, - version: "0", - }, - }; - - const mockPurposeUpdateContent: ApiPurposeUpdateContent = { - ...purposeUpdateContent, - riskAnalysisForm: buildRiskAnalysisSeed(invalidRiskAnalysis), - }; - - expect( - purposeService.updatePurpose({ - purposeId: purposeForDeliver.id, - purposeUpdateContent: mockPurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError( - riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) - ); - }); - it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid in updateReversePurpose", async () => { - const purposeWithInvalidRiskAnalysis: Purpose = { - ...purposeForReceive, - riskAnalysisForm: { - ...purposeForReceive.riskAnalysisForm!, - version: "0", - }, - }; - - await addOnePurpose( - purposeWithInvalidRiskAnalysis, - postgresDB, - purposes - ); - await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); - await writeInReadmodel(tenant, tenants); - - expect( - purposeService.updateReversePurpose({ - purposeId: purposeWithInvalidRiskAnalysis.id, - reversePurposeUpdateContent, - organizationId: tenant.id, - correlationId: generateId(), - }) - ).rejects.toThrowError( - riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) - ); - }); - }); - + testUpdatePurpose(); }); }); diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts new file mode 100644 index 0000000000..62c751edef --- /dev/null +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -0,0 +1,422 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { unexpectedRulesVersionError } from "pagopa-interop-commons"; +import { + randomArrayItem, + getMockTenant, + getMockPurpose, + getMockPurposeVersion, + getMockValidRiskAnalysisForm, + getMockValidRiskAnalysis, + writeInReadmodel, + readLastEventByStreamId, + decodeProtobufPayload, +} from "pagopa-interop-commons-test/index.js"; +import { + tenantKind, + Tenant, + EService, + Purpose, + purposeVersionState, + generateId, + toReadModelEService, + DraftPurposeUpdatedV2, + toPurposeV2, + PurposeId, + unsafeBrandId, + TenantId, + EServiceId, + RiskAnalysis, +} from "pagopa-interop-models"; +import { describe, it, expect } from "vitest"; +import { + purposeNotFound, + organizationIsNotTheConsumer, + purposeNotInDraftState, + eserviceNotFound, + eServiceModeNotAllowed, + missingFreeOfChargeReason, + tenantNotFound, + tenantKindNotFound, + riskAnalysisValidationFailed, +} from "../src/model/domain/errors.js"; +import { + ApiPurposeUpdateContent, + ApiReversePurposeUpdateContent, +} from "../src/model/domain/models.js"; +import { + postgresDB, + purposes, + eservices, + tenants, + purposeService, +} from "./purposeService.integration.test.js"; +import { + getMockEService, + buildRiskAnalysisSeed, + addOnePurpose, + createUpdatedPurpose, +} from "./utils.js"; + +export const testUpdatePurpose = (): ReturnType => + describe("updatePurpose and updateReversePurpose", () => { + const tenantType = randomArrayItem(Object.values(tenantKind)); + const tenant: Tenant = { + ...getMockTenant(), + kind: tenantType, + }; + + const eServiceDeliver: EService = { + ...getMockEService(), + mode: "Deliver", + }; + + const eServiceReceive: EService = { + ...getMockEService(), + mode: "Receive", + producerId: tenant.id, + }; + + const purposeForReceive: Purpose = { + ...getMockPurpose(), + eserviceId: eServiceReceive.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + riskAnalysisForm: { + ...getMockValidRiskAnalysisForm(tenantType), + id: generateId(), + }, + }; + + const purposeForDeliver: Purpose = { + ...getMockPurpose(), + eserviceId: eServiceDeliver.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + }; + + const validRiskAnalysis = getMockValidRiskAnalysis(tenantType); + + const purposeUpdateContent: ApiPurposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: true, + freeOfChargeReason: "reason", + riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), + }; + + const reversePurposeUpdateContent: ApiReversePurposeUpdateContent = { + ...purposeUpdateContent, + }; + + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + await purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + purposeForDeliver.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: purposeForDeliver.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForDeliver, + purposeUpdateContent, + validRiskAnalysis, + writtenPayload + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); + + await purposeService.updateReversePurpose({ + purposeId: purposeForReceive.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + purposeForReceive.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: purposeForReceive.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForReceive, + reversePurposeUpdateContent, + validRiskAnalysis, + writtenPayload + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + it("Should throw purposeNotFound if the purpose doesn't exist", async () => { + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const purposeId: PurposeId = unsafeBrandId(generateId()); + + expect( + purposeService.updatePurpose({ + purposeId, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(purposeId)); + }); + it("Should throw organizationIsNotTheConsumer if the organization is not the consumer", async () => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + consumerId: generateId(), + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const organizationId: TenantId = unsafeBrandId(generateId()); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId, + correlationId: generateId(), + }) + ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); + }); + it.each( + Object.values(purposeVersionState).filter( + (state) => state !== purposeVersionState.draft + ) + )( + "Should throw purposeNotInDraftState if the purpose is in state %s", + async (state) => { + const mockPurpose: Purpose = { + ...purposeForDeliver, + versions: [{ ...getMockPurposeVersion(state) }], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); + } + ); + it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { + const eserviceId: EServiceId = unsafeBrandId(generateId()); + const mockPurpose: Purpose = { + ...purposeForDeliver, + eserviceId, + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: mockPurpose.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(eserviceNotFound(eserviceId)); + }); + it("should throw eServiceModeNotAllowed if the eService mode is incorrect when expecting DELIVER", async () => { + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForReceive.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + eServiceModeNotAllowed(eServiceReceive.id, "Deliver") + ); + }); + it("should throw eServiceModeNotAllowed if the eService mode is incorrect when expecting RECEIVE", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeForDeliver.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + eServiceModeNotAllowed(eServiceDeliver.id, "Receive") + ); + }); + it("Should throw missingFreeOfChargeReason if the freeOfChargeReason is missing", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: { + ...purposeUpdateContent, + freeOfChargeReason: undefined, + }, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(missingFreeOfChargeReason()); + }); + it("Should throw tenantNotFound if the tenant does not exist", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + + await addOnePurpose(purposeForReceive, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeForReceive.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + }); + it("Should throw tenantKindNotFound if the tenant kind does not exist", async () => { + const mockTenant = { + ...tenant, + kind: undefined, + }; + + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent, + organizationId: mockTenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid in updatePurpose", async () => { + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const invalidRiskAnalysis: RiskAnalysis = { + ...validRiskAnalysis, + riskAnalysisForm: { + ...validRiskAnalysis.riskAnalysisForm, + version: "0", + }, + }; + + const mockPurposeUpdateContent: ApiPurposeUpdateContent = { + ...purposeUpdateContent, + riskAnalysisForm: buildRiskAnalysisSeed(invalidRiskAnalysis), + }; + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: mockPurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) + ); + }); + it("Should throw riskAnalysisValidationFailed if the risk analysis is not valid in updateReversePurpose", async () => { + const purposeWithInvalidRiskAnalysis: Purpose = { + ...purposeForReceive, + riskAnalysisForm: { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ...purposeForReceive.riskAnalysisForm!, + version: "0", + }, + }; + + await addOnePurpose(purposeWithInvalidRiskAnalysis, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); + await writeInReadmodel(tenant, tenants); + + expect( + purposeService.updateReversePurpose({ + purposeId: purposeWithInvalidRiskAnalysis.id, + reversePurposeUpdateContent, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) + ); + }); + }); From 74ed2994ec5bcc04a5e2460560029a0a6b75568b Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Sat, 20 Apr 2024 16:54:03 +0200 Subject: [PATCH 276/537] WIP --- packages/models/package.json | 3 + packages/models/src/purpose/purpose.ts | 11 ++ .../src/model/domain/models.ts | 2 + .../src/services/purposeService.ts | 32 +++- .../src/services/readModelService.ts | 153 +++++++++++++++++- 5 files changed, 199 insertions(+), 2 deletions(-) diff --git a/packages/models/package.json b/packages/models/package.json index f883772078..f8164b7adc 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -8,6 +8,9 @@ "exports": { ".": "./dist/index.js" }, + "config": { + "protocVersion": "26.1" + }, "scripts": { "lint": "eslint . --ext .ts,.tsx", "lint:autofix": "eslint . --ext .ts,.tsx --fix", diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index feecc4e13c..26944ba510 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -61,6 +61,17 @@ export const Purpose = z.object({ }); export type Purpose = z.infer; +export const PurposeSeed = z.object({ + eserviceId: EServiceId, + consumerId: TenantId, + riskAnalysisForm: PurposeRiskAnalysisForm.optional(), + title: z.string(), + description: z.string(), + isFreeOfCharge: z.boolean(), + freeOfChargeReason: z.string().optional(), +}); +export type PurposeSeed = z.infer; + export const ownership = { CONSUMER: "CONSUMER", PRODUCER: "PRODUCER", diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index ea3bae9295..63b84aaac4 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -9,6 +9,8 @@ import * as api from "../generated/api.js"; export type ApiRiskAnalysisForm = z.infer; export type ApiPurposeVersion = z.infer; export type ApiPurpose = z.infer; +export type ApiPurposeSeed = z.infer; + export type ApiPurposeVersionState = z.infer< typeof api.schemas.PurposeVersionState >; diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d3600bc824..2d121de2a2 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -25,10 +25,12 @@ import { PurposeEvent, EServiceMode, ListResult, + PurposeSeed, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { eserviceNotFound, + missingFreeOfChargeReason, notValidVersionState, organizationIsNotTheConsumer, organizationIsNotTheProducer, @@ -37,6 +39,7 @@ import { purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, + tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; import { @@ -54,7 +57,7 @@ import { ApiReversePurposeUpdateContent, ApiGetPurposesFilters, } from "../model/domain/models.js"; -import { ReadModelService } from "./readModelService.js"; +import { AgreementQueryFilters, ReadModelService } from "./readModelService.js"; import { assertOrganizationIsAConsumer, assertEserviceHasSpecificMode, @@ -522,6 +525,33 @@ export function purposeServiceBuilder( totalCount: purposesList.totalCount, }; }, + async createPurpose( + purposeSeed: PurposeSeed, + organizationId: TenantId + ): Promise { + logger.info( + `Creating Purpose for EService ${purposeSeed.eserviceId} and Consumer ${purposeSeed.consumerId}` + ); + assertOrganizationIsAConsumer(organizationId, purposeSeed.consumerId); + + if (purposeSeed.isFreeOfCharge && !purposeSeed.freeOfChargeReason) { + throw missingFreeOfChargeReason(); + } + const tenant = await retrieveTenant(organizationId, readModelService); + + if (!tenant.kind) { + throw tenantKindNotFound(tenant.id); + } + + const clientSeed; + + const filters: AgreementQueryFilters; + + const agreementsx = await readModelService.getAllAgreements( + agreements, + filters + ); + }, }; } diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 992f20cad5..1f86e2a283 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -5,6 +5,9 @@ import { TenantCollection, PurposeCollection, ReadModelFilter, + AgreementCollection, + MongoQueryKeys, + RemoveDataPrefix, } from "pagopa-interop-commons"; import { EService, @@ -18,11 +21,153 @@ import { PurposeId, ListResult, purposeVersionState, + Agreement, + AgreementState, + AttributeId, + DescriptorId, + agreementState, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; +import { match, P } from "ts-pattern"; import { ApiGetPurposesFilters } from "../model/domain/models.js"; +export type AgreementQueryFilters = { + producerId?: string | string[]; + consumerId?: string | string[]; + eserviceId?: EServiceId | EServiceId[]; + descriptorId?: DescriptorId | DescriptorId[]; + agreementStates?: AgreementState[]; + attributeId?: AttributeId | AttributeId[]; + showOnlyUpgradeable?: boolean; +}; + +type AgreementDataFields = RemoveDataPrefix>; + +const makeFilter = ( + fieldName: Extract< + AgreementDataFields, + "producerId" | "consumerId" | "eserviceId" | "descriptorId" + >, + value: string | string[] | undefined +): ReadModelFilter | undefined => + match(value) + .with(P.nullish, () => undefined) + .with(P.string, () => ({ + [`data.${fieldName}`]: value, + })) + .with(P.array(P.string), (a) => + a.length === 0 ? undefined : { [`data.${fieldName}`]: { $in: value } } + ) + .exhaustive(); + +const makeAttributesFilter = ( + fieldName: Extract< + AgreementDataFields, + "certifiedAttributes" | "declaredAttributes" | "verifiedAttributes" + >, + attributeIds: AttributeId | AttributeId[] +): ReadModelFilter | undefined => + match(attributeIds) + .with(P.string, (id) => ({ + [`data.${fieldName}`]: { $elemMatch: { id } }, + })) + .with(P.array(P.string), (ids) => + ids.length === 0 + ? undefined + : { + [`data.${fieldName}`]: { + $elemMatch: { id: { $in: ids } }, + }, + } + ) + .exhaustive(); + +const getAgreementsFilters = ( + filters: AgreementQueryFilters +): { $match: object } => { + const upgradeableStates = [ + agreementState.draft, + agreementState.active, + agreementState.suspended, + ]; + + const { + attributeId, + producerId, + consumerId, + eserviceId, + descriptorId, + agreementStates, + showOnlyUpgradeable, + } = filters; + + const agreementStatesFilters = match(agreementStates) + .with(P.nullish, () => (showOnlyUpgradeable ? upgradeableStates : [])) + .with( + P.when( + (agreementStates) => agreementStates.length === 0 && showOnlyUpgradeable + ), + () => upgradeableStates + ) + .with( + P.when( + (agreementStates) => agreementStates.length > 0 && showOnlyUpgradeable + ), + (agreementStates) => + upgradeableStates.filter((s) => agreementStates.includes(s)) + ) + .otherwise((agreementStates) => agreementStates); + + const queryFilters = { + ...makeFilter("producerId", producerId), + ...makeFilter("consumerId", consumerId), + ...makeFilter("eserviceId", eserviceId), + ...makeFilter("descriptorId", descriptorId), + ...(agreementStatesFilters && + agreementStatesFilters.length > 0 && { + "data.state": { + $in: agreementStatesFilters.map((s) => s.toString()), + }, + }), + ...(attributeId && { + $or: [ + makeAttributesFilter("certifiedAttributes", attributeId), + makeAttributesFilter("verifiedAttributes", attributeId), + makeAttributesFilter("declaredAttributes", attributeId), + ], + }), + }; + return { $match: queryFilters }; +}; + +const getAllAgreementsConst = async ( + agreements: AgreementCollection, + filters: AgreementQueryFilters +): Promise => { + const data = await agreements + .aggregate([getAgreementsFilters(filters)]) + .toArray(); + + const result = z + .array( + z.object({ + data: Agreement, + }) + ) + .safeParse(data); + + if (!result.success) { + logger.error( + `Unable to parse agreements items: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse agreements items"); + } + return result.data.map((d) => d.data); +}; + async function getPurpose( purposes: PurposeCollection, filter: Filter>> @@ -101,7 +246,7 @@ async function getTenant( export function readModelServiceBuilder( readModelRepository: ReadModelRepository ) { - const { eservices, purposes, tenants } = readModelRepository; + const { eservices, purposes, tenants, agreements } = readModelRepository; return { async getEServiceById(id: EServiceId): Promise { @@ -115,6 +260,12 @@ export function readModelServiceBuilder( ): Promise | undefined> { return getPurpose(purposes, { "data.id": id }); }, + async getAllAgreements( + agreements: AgreementCollection, + filters: AgreementQueryFilters + ): Promise { + return getAllAgreementsConst(agreements, filters); + }, async getPurposes( filters: ApiGetPurposesFilters, offset: number, From 144b242586ae6ee0936b10a4be3507dc8fa99c1e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 09:42:23 +0200 Subject: [PATCH 277/537] Adjust test utils --- packages/commons-test/src/testUtils.ts | 24 +++++++----------------- packages/purpose-process/test/utils.ts | 10 ++++------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 59f8f87724..a408052e71 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -123,13 +123,16 @@ export const getMockTenant = ( tenantId: TenantId = generateId(), attributes: TenantAttribute[] = [] ): Tenant => ({ - ...generateMock(Tenant), + name: "A tenant", id: tenantId, + createdAt: new Date(), + attributes, externalId: { - value: uuidv4(), - origin: "EXT", + value: "123456", + origin: "IPA", }, - attributes, + features: [], + mails: [], }); export const getMockAgreement = ( @@ -186,16 +189,3 @@ export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ origin: "IPA", }, }); - -export const getMockTenant = (): Tenant => ({ - name: "A tenant", - id: generateId(), - createdAt: new Date(), - attributes: [], - externalId: { - value: "123456", - origin: "IPA", - }, - features: [], - mails: [], -}); diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 885b604305..82dc46e462 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -1,5 +1,6 @@ import { PurposeCollection } from "pagopa-interop-commons"; import { + StoredEvent, writeInEventstore, writeInReadmodel, } from "pagopa-interop-commons-test"; @@ -8,7 +9,6 @@ import { Purpose, PurposeEvent, generateId, - purposeEventToBinaryData, technology, toPurposeV2, } from "pagopa-interop-models"; @@ -32,13 +32,11 @@ export const writePurposeInEventstore = async ( event_version: 2, data: { purpose: toPurposeV2(purpose) }, }; - const eventToWrite = { + const eventToWrite: StoredEvent = { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion stream_id: purposeEvent.data.purpose!.id, - version: "0", - type: purposeEvent.type, - event_version: purposeEvent.event_version, - data: purposeEventToBinaryData(purposeEvent), + version: 0, + event: purposeEvent, }; await writeInEventstore(eventToWrite, "purpose", postgresDB); From c1dfed72750c6f0ad94c2ee8dcf07f1e627befae Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 09:45:54 +0200 Subject: [PATCH 278/537] Fix utils --- packages/commons-test/src/eventStoreTestUtils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/commons-test/src/eventStoreTestUtils.ts b/packages/commons-test/src/eventStoreTestUtils.ts index 75fb362871..06ef5d55cf 100644 --- a/packages/commons-test/src/eventStoreTestUtils.ts +++ b/packages/commons-test/src/eventStoreTestUtils.ts @@ -9,12 +9,14 @@ import { EServiceId, EventStoreSchema, PurposeEvent, + PurposeId, TenantEvent, TenantId, agreementEventToBinaryData, attributeEventToBinaryData, catalogEventToBinaryData, protobufDecoder, + purposeEventToBinaryData, tenantEventToBinaryData, } from "pagopa-interop-models"; import { Event } from "pagopa-interop-commons"; @@ -69,9 +71,9 @@ export async function writeInEventstore( .with("tenant", () => tenantEventToBinaryData(event.event as TenantEvent) ) - .with("purpose", () => { - throw new Error("Purpose events not implemented yet"); - }) + .with("purpose", () => + purposeEventToBinaryData(event.event as PurposeEvent) + ) .exhaustive(), ] ); @@ -87,7 +89,7 @@ export async function readLastEventByStreamId( : T extends "tenant" ? TenantId : T extends "purpose" - ? never // Purpose events not implemented yet + ? PurposeId : never, schema: T, postgresDB: IDatabase @@ -102,7 +104,7 @@ export async function readLastEventByStreamId( : T extends "tenant" ? TenantEvent : T extends "purpose" - ? never // Purpose events not implemented yet + ? PurposeEvent : never > > { From d39fb9e8d0dba0a74c1927b886a4e59e465f09f9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 09:55:14 +0200 Subject: [PATCH 279/537] Update logic --- packages/purpose-process/src/services/purposeService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d95695320a..2b81b00fb0 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -180,7 +180,10 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); - if (purposeVersion.state !== purposeVersionState.waitingForApproval) { + if ( + purposeVersion.state !== purposeVersionState.waitingForApproval || + purpose.data.versions.length > 1 + ) { throw purposeVersionCannotBeDeleted(purposeId, versionId); } From cd1511a349cc6fde28c753ecab4f9fff9a6b6629 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 09:55:40 +0200 Subject: [PATCH 280/537] Add test --- .../test/testDeletePurposeVersion.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index eaab7b6663..fd0dd98fb3 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -172,6 +172,39 @@ export const testDeletePurposeVersion = (): ReturnType => await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + } + ); + it.each( + Object.values(purposeVersionState).filter( + (state) => state !== purposeVersionState.waitingForApproval + ) + )( + "should throw purposeVersionCannotBeDeleted if the purpose has also another version in %s state", + async (state) => { + const mockPurposeVersion = getMockPurposeVersion(state); + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [ + getMockPurposeVersion(purposeVersionState.waitingForApproval), + mockPurposeVersion, + ], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + expect( purposeService.deletePurposeVersion({ purposeId: mockPurpose.id, From 078bf910d6955a73a948c26848649c2e6e611224 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 10:02:45 +0200 Subject: [PATCH 281/537] Fix logic and tests --- .../src/services/purposeService.ts | 2 +- .../test/testDeletePurposeVersion.ts | 78 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 2b81b00fb0..499e412ba2 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -182,7 +182,7 @@ export function purposeServiceBuilder( if ( purposeVersion.state !== purposeVersionState.waitingForApproval || - purpose.data.versions.length > 1 + purpose.data.versions.length === 1 ) { throw purposeVersionCannotBeDeleted(purposeId, versionId); } diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index fd0dd98fb3..9ec8f3de55 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -38,14 +38,16 @@ export const testDeletePurposeVersion = (): ReturnType => vi.setSystemTime(new Date()); const mockEService = getMockEService(); - const mockPurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.waitingForApproval, - }; + const mockPurposeVersion1 = getMockPurposeVersion( + purposeVersionState.waitingForApproval + ); + const mockPurposeVersion2 = getMockPurposeVersion( + purposeVersionState.draft + ); const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [mockPurposeVersion1, mockPurposeVersion2], }; await addOnePurpose(mockPurpose, postgresDB, purposes); @@ -53,7 +55,7 @@ export const testDeletePurposeVersion = (): ReturnType => await purposeService.deletePurposeVersion({ purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, + versionId: mockPurposeVersion1.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), }); @@ -78,7 +80,7 @@ export const testDeletePurposeVersion = (): ReturnType => const expectedPurpose: Purpose = { ...mockPurpose, - versions: [], + versions: [mockPurposeVersion2], updatedAt: new Date(), }; @@ -137,7 +139,10 @@ export const testDeletePurposeVersion = (): ReturnType => const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, - versions: [mockPurposeVersion], + versions: [ + mockPurposeVersion, + getMockPurposeVersion(purposeVersionState.draft), + ], }; await addOnePurpose(mockPurpose, postgresDB, purposes); @@ -160,36 +165,6 @@ export const testDeletePurposeVersion = (): ReturnType => ) )( "should throw purposeVersionCannotBeDeleted if the purpose version is in %s state", - async (state) => { - const mockPurposeVersion = getMockPurposeVersion(state); - const mockEService = getMockEService(); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - expect( - purposeService.deletePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) - ); - } - ); - it.each( - Object.values(purposeVersionState).filter( - (state) => state !== purposeVersionState.waitingForApproval - ) - )( - "should throw purposeVersionCannotBeDeleted if the purpose has also another version in %s state", async (state) => { const mockPurposeVersion = getMockPurposeVersion(state); const mockEService = getMockEService(); @@ -197,8 +172,8 @@ export const testDeletePurposeVersion = (): ReturnType => ...getMockPurpose(), eserviceId: mockEService.id, versions: [ - getMockPurposeVersion(purposeVersionState.waitingForApproval), mockPurposeVersion, + getMockPurposeVersion(purposeVersionState.draft), ], }; @@ -217,4 +192,29 @@ export const testDeletePurposeVersion = (): ReturnType => ); } ); + it("should throw purposeVersionCannotBeDeleted if the purpose has only that version", async () => { + const mockPurposeVersion = getMockPurposeVersion( + purposeVersionState.waitingForApproval + ); + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) + ); + }); }); From 0387d7561c8af07c17c96bfac7a558800af920e3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 10:27:18 +0200 Subject: [PATCH 282/537] Split and improve test --- .../purpose-process/test/testDeletePurpose.ts | 217 ++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 packages/purpose-process/test/testDeletePurpose.ts diff --git a/packages/purpose-process/test/testDeletePurpose.ts b/packages/purpose-process/test/testDeletePurpose.ts new file mode 100644 index 0000000000..747f727c70 --- /dev/null +++ b/packages/purpose-process/test/testDeletePurpose.ts @@ -0,0 +1,217 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { describe, expect, it } from "vitest"; +import { + DraftPurposeDeletedV2, + Purpose, + PurposeId, + PurposeVersion, + WaitingForApprovalPurposeDeletedV2, + generateId, + purposeVersionState, + toPurposeV2, + toReadModelEService, +} from "pagopa-interop-models"; +import { + getMockPurpose, + writeInReadmodel, + readLastEventByStreamId, + decodeProtobufPayload, + getMockPurposeVersion, +} from "pagopa-interop-commons-test/index.js"; +import { + purposeNotFound, + organizationIsNotTheConsumer, + purposeCannotBeDeleted, +} from "../src/model/domain/errors.js"; +import { + eservices, + postgresDB, + purposeService, + purposes, +} from "./purposeService.integration.test.js"; +import { addOnePurpose, getMockEService } from "./utils.js"; + +describe("deletePurpose", () => { + it("should write on event-store for the deletion of a purpose (no versions)", async () => { + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); + }); + it("should write on event-store for the deletion of a purpose (draft version)", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(purposeVersionState.draft); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); + }); + it("should write on event-store for the deletion of a purpose (waitingForApproval version)", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion( + purposeVersionState.waitingForApproval + ); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "WaitingForApprovalPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: WaitingForApprovalPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomId: PurposeId = generateId(); + const mockPurpose = getMockPurpose(); + + await addOnePurpose(mockPurpose, postgresDB, purposes); + expect( + purposeService.deletePurpose({ + purposeId: randomId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomId)); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = getMockPurposeVersion( + purposeVersionState.draft + ); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheConsumer(mockEService.producerId) + ); + }); + it.each( + Object.values(purposeVersionState).filter( + (state) => + state !== purposeVersionState.waitingForApproval && + state !== purposeVersionState.draft + ) + )( + "should throw purposeCannotBeDeleted if the purpose has a $s version ", + async (state) => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(state); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); + } + ); +}); From 233412c8bfbcf72ead9a1cd0ff8b500dc928bcca Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 10:46:44 +0200 Subject: [PATCH 283/537] Split test file --- .../test/testArchivePurposeVersion.ts | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 packages/purpose-process/test/testArchivePurposeVersion.ts diff --git a/packages/purpose-process/test/testArchivePurposeVersion.ts b/packages/purpose-process/test/testArchivePurposeVersion.ts new file mode 100644 index 0000000000..064ff9896f --- /dev/null +++ b/packages/purpose-process/test/testArchivePurposeVersion.ts @@ -0,0 +1,278 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + getMockPurposeVersion, + getMockPurpose, + readLastEventByStreamId, + decodeProtobufPayload, +} from "pagopa-interop-commons-test/index.js"; +import { + PurposeVersion, + purposeVersionState, + Purpose, + generateId, + PurposeArchivedV2, + toPurposeV2, + PurposeId, + PurposeVersionId, + TenantId, +} from "pagopa-interop-models"; +import { describe, expect, it, vi } from "vitest"; +import { + purposeNotFound, + organizationIsNotTheConsumer, + purposeVersionNotFound, + notValidVersionState, +} from "../src/model/domain/errors.js"; +import { + postgresDB, + purposes, + purposeService, +} from "./purposeService.integration.test.js"; +import { addOnePurpose } from "./utils.js"; + +export const testArchivePurposeVersion = (): ReturnType => + describe("archivePurposeVersion", () => { + it("should write on event-store for the archiving of a purpose version", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + + await purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeArchived", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.rejected, + updatedAt: new Date(), + }, + ], + updatedAt: new Date(), + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeArchivedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should write on event-store for the archiving of a purpose version, and delete waitingForApproval versions", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurposeVersion2: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion1, mockPurposeVersion2], + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + + await purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeArchived", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.rejected, + updatedAt: new Date(), + }, + ], + updatedAt: new Date(), + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeArchivedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomPurposeId: PurposeId = generateId(); + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose = getMockPurpose(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: randomPurposeId, + versionId: randomVersionId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomPurposeId)); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const randomOrganizationId: TenantId = generateId(); + + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: randomOrganizationId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + organizationIsNotTheConsumer(randomOrganizationId) + ); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: randomVersionId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose.id, randomVersionId) + ); + }); + it("should throw notValidVersionState if the purpose version is in draft state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in waitingForApproval state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + it("should throw notValidVersionState if the purpose version is in rejected state", async () => { + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + }); + }); From e50d9875b90fa4a7e9933932168043082de544c0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 10:48:00 +0200 Subject: [PATCH 284/537] Fix --- .../test/purposeService.integration.test.ts | 25 +- .../purpose-process/test/testDeletePurpose.ts | 319 +++++++++--------- 2 files changed, 163 insertions(+), 181 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 0b6f63e796..bf5cf2e508 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -19,29 +19,6 @@ import { postgreSQLContainer, } from "pagopa-interop-commons-test"; import { StartedTestContainer } from "testcontainers"; -import { - DraftPurposeDeletedV2, - DraftPurposeUpdatedV2, - EService, - EServiceId, - Purpose, - PurposeId, - PurposeVersion, - PurposeVersionDocumentId, - PurposeVersionId, - PurposeVersionRejectedV2, - RiskAnalysis, - Tenant, - TenantId, - WaitingForApprovalPurposeDeletedV2, - WaitingForApprovalPurposeVersionDeletedV2, - generateId, - purposeVersionState, - tenantKind, - toPurposeV2, - toReadModelEService, - unsafeBrandId, -} from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { PurposeService, @@ -56,6 +33,7 @@ import { testGetRiskAnalysisDocument } from "./testGetRiskAnalysisDocument.js"; import { testDeletePurposeVersion } from "./testDeletePurposeVersion.js"; import { testRejectPurposeVersion } from "./testRejectPurposeVersion.js"; import { testUpdatePurpose } from "./testUpdatePurpose.js"; +import { testDeletePurpose } from "./testDeletePurpose.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -113,5 +91,6 @@ describe("Integration tests", async () => { testDeletePurposeVersion(); testRejectPurposeVersion(); testUpdatePurpose(); + testDeletePurpose(); }); }); diff --git a/packages/purpose-process/test/testDeletePurpose.ts b/packages/purpose-process/test/testDeletePurpose.ts index 747f727c70..d6ed825a75 100644 --- a/packages/purpose-process/test/testDeletePurpose.ts +++ b/packages/purpose-process/test/testDeletePurpose.ts @@ -31,171 +31,143 @@ import { } from "./purposeService.integration.test.js"; import { addOnePurpose, getMockEService } from "./utils.js"; -describe("deletePurpose", () => { - it("should write on event-store for the deletion of a purpose (no versions)", async () => { - const mockEService = getMockEService(); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - await purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }); - - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "DraftPurposeDeleted", - event_version: 2, - }); - - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeDeletedV2, - payload: writtenEvent.data, - }); - - expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); - }); - it("should write on event-store for the deletion of a purpose (draft version)", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(purposeVersionState.draft); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - await purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }); - - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "DraftPurposeDeleted", - event_version: 2, - }); - - const writtenPayload = decodeProtobufPayload({ - messageType: DraftPurposeDeletedV2, - payload: writtenEvent.data, - }); - - expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); - }); - it("should write on event-store for the deletion of a purpose (waitingForApproval version)", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion( - purposeVersionState.waitingForApproval - ); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - - await purposeService.deletePurpose({ - purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }); +export const testDeletePurpose = (): ReturnType => + describe("deletePurpose", () => { + it("should write on event-store for the deletion of a purpose (no versions)", async () => { + const mockEService = getMockEService(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [], + }; - const writtenEvent = await readLastEventByStreamId( - mockPurpose.id, - "purpose", - postgresDB - ); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect(writtenEvent).toMatchObject({ - stream_id: mockPurpose.id, - version: "1", - type: "WaitingForApprovalPurposeDeleted", - event_version: 2, + await purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); }); + it("should write on event-store for the deletion of a purpose (draft version)", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion( + purposeVersionState.draft + ); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - const writtenPayload = decodeProtobufPayload({ - messageType: WaitingForApprovalPurposeDeletedV2, - payload: writtenEvent.data, - }); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); - }); - it("should throw purposeNotFound if the purpose doesn't exist", async () => { - const randomId: PurposeId = generateId(); - const mockPurpose = getMockPurpose(); - - await addOnePurpose(mockPurpose, postgresDB, purposes); - expect( - purposeService.deletePurpose({ - purposeId: randomId, + await purposeService.deletePurpose({ + purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), - }) - ).rejects.toThrowError(purposeNotFound(randomId)); - }); - it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { - const mockEService = getMockEService(); - const mockPurposeVersion: PurposeVersion = getMockPurposeVersion( - purposeVersionState.draft - ); - const mockPurpose: Purpose = { - ...getMockPurpose(), - eserviceId: mockEService.id, - versions: [mockPurposeVersion], - }; + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "DraftPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); + }); + it("should write on event-store for the deletion of a purpose (waitingForApproval version)", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion( + purposeVersionState.waitingForApproval + ); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; - await addOnePurpose(mockPurpose, postgresDB, purposes); - await writeInReadmodel(toReadModelEService(mockEService), eservices); + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); - expect( - purposeService.deletePurpose({ + await purposeService.deletePurpose({ purposeId: mockPurpose.id, - organizationId: mockEService.producerId, + organizationId: mockPurpose.consumerId, correlationId: generateId(), - }) - ).rejects.toThrowError( - organizationIsNotTheConsumer(mockEService.producerId) - ); - }); - it.each( - Object.values(purposeVersionState).filter( - (state) => - state !== purposeVersionState.waitingForApproval && - state !== purposeVersionState.draft - ) - )( - "should throw purposeCannotBeDeleted if the purpose has a $s version ", - async (state) => { - const mockEService = getMockEService(); - const mockPurposeVersion = getMockPurposeVersion(state); + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "WaitingForApprovalPurposeDeleted", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: WaitingForApprovalPurposeDeletedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(mockPurpose)); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomId: PurposeId = generateId(); + const mockPurpose = getMockPurpose(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + expect( + purposeService.deletePurpose({ + purposeId: randomId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomId)); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = getMockPurposeVersion( + purposeVersionState.draft + ); const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, @@ -208,10 +180,41 @@ describe("deletePurpose", () => { expect( purposeService.deletePurpose({ purposeId: mockPurpose.id, - organizationId: mockPurpose.consumerId, + organizationId: mockEService.producerId, correlationId: generateId(), }) - ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); - } - ); -}); + ).rejects.toThrowError( + organizationIsNotTheConsumer(mockEService.producerId) + ); + }); + it.each( + Object.values(purposeVersionState).filter( + (state) => + state !== purposeVersionState.waitingForApproval && + state !== purposeVersionState.draft + ) + )( + "should throw purposeCannotBeDeleted if the purpose has a $s version ", + async (state) => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(state); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.deletePurpose({ + purposeId: mockPurpose.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); + } + ); + }); From 1cc8cc390ea3be78e3e0a790522631b0bb99aa93 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 10:52:40 +0200 Subject: [PATCH 285/537] Improve test --- .../test/testArchivePurposeVersion.ts | 99 ++++++------------- 1 file changed, 30 insertions(+), 69 deletions(-) diff --git a/packages/purpose-process/test/testArchivePurposeVersion.ts b/packages/purpose-process/test/testArchivePurposeVersion.ts index 064ff9896f..93426246e6 100644 --- a/packages/purpose-process/test/testArchivePurposeVersion.ts +++ b/packages/purpose-process/test/testArchivePurposeVersion.ts @@ -206,73 +206,34 @@ export const testArchivePurposeVersion = (): ReturnType => purposeVersionNotFound(mockPurpose.id, randomVersionId) ); }); - it("should throw notValidVersionState if the purpose version is in draft state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.draft, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - - expect( - purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); - it("should throw notValidVersionState if the purpose version is in waitingForApproval state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.waitingForApproval, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - - expect( - purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); - it("should throw notValidVersionState if the purpose version is in rejected state", async () => { - const mockPurposeVersion: PurposeVersion = { - ...getMockPurposeVersion(), - state: purposeVersionState.rejected, - }; - const mockPurpose: Purpose = { - ...getMockPurpose(), - versions: [mockPurposeVersion], - }; - - await addOnePurpose(mockPurpose, postgresDB, purposes); - - expect( - purposeService.archivePurposeVersion({ - purposeId: mockPurpose.id, - versionId: mockPurposeVersion.id, - organizationId: mockPurpose.consumerId, - correlationId: generateId(), - }) - ).rejects.toThrowError( - notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) - ); - }); + it.each( + Object.values(purposeVersionState).filter( + (state) => + state !== purposeVersionState.active && + state !== purposeVersionState.suspended + ) + )( + "should throw notValidVersionState if the purpose version is in %s state", + async (state) => { + const mockPurposeVersion = getMockPurposeVersion(state); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.archivePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + } + ); }); From 014ea4b9f88c40cdf69d5dcaa22f3f0afc0738b9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 10:57:15 +0200 Subject: [PATCH 286/537] Split and improve test --- .../test/testSuspendPurposeVersion.ts | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 packages/purpose-process/test/testSuspendPurposeVersion.ts diff --git a/packages/purpose-process/test/testSuspendPurposeVersion.ts b/packages/purpose-process/test/testSuspendPurposeVersion.ts new file mode 100644 index 0000000000..3c8f1743d7 --- /dev/null +++ b/packages/purpose-process/test/testSuspendPurposeVersion.ts @@ -0,0 +1,256 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { describe, expect, it, vi } from "vitest"; +import { + getMockPurposeVersion, + getMockPurpose, + writeInReadmodel, + readLastEventByStreamId, + decodeProtobufPayload, +} from "pagopa-interop-commons-test/index.js"; +import { + PurposeVersion, + purposeVersionState, + Purpose, + toReadModelEService, + generateId, + PurposeVersionSuspendedByConsumerV2, + toPurposeV2, + PurposeVersionSuspendedByProducerV2, + PurposeId, + PurposeVersionId, + TenantId, +} from "pagopa-interop-models"; +import { + purposeNotFound, + purposeVersionNotFound, + organizationNotAllowed, + notValidVersionState, +} from "../src/model/domain/errors.js"; +import { addOnePurpose, getMockEService } from "./utils.js"; +import { + postgresDB, + purposes, + eservices, + purposeService, +} from "./purposeService.integration.test.js"; + +export const testSuspendPurposeVersion = (): ReturnType => + describe("suspendPurposeVersion", () => { + it("should write on event-store for the suspension of a purpose version by the consumer", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion1], + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeVersionSuspendedByConsumer", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.suspended, + suspendedAt: new Date(), + updatedAt: new Date(), + }, + ], + updatedAt: new Date(), + suspendedByConsumer: true, + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionSuspendedByConsumerV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should write on event-store for the suspension of a purpose version by the producer", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion1], + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeVersionSuspendedByProducer", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.suspended, + suspendedAt: new Date(), + updatedAt: new Date(), + }, + ], + suspendedByProducer: true, + updatedAt: new Date(), + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionSuspendedByProducerV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw purposeNotFound if the purpose doesn't exist", async () => { + const randomPurposeId: PurposeId = generateId(); + const randomVersionId: PurposeVersionId = generateId(); + const mockPurpose = getMockPurpose(); + await addOnePurpose(mockPurpose, postgresDB, purposes); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: randomPurposeId, + versionId: randomVersionId, + organizationId: generateId(), + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(randomPurposeId)); + }); + it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { + const mockEService = getMockEService(); + const randomVersionId: PurposeVersionId = generateId(); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: randomVersionId, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + purposeVersionNotFound(mockPurpose.id, randomVersionId) + ); + }); + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { + const mockEService = getMockEService(); + const randomId: TenantId = generateId(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: randomId, + correlationId: generateId(), + }) + ).rejects.toThrowError(organizationNotAllowed(randomId)); + }); + it.each( + Object.values(purposeVersionState).filter( + (state) => + state !== purposeVersionState.active && + state !== purposeVersionState.suspended + ) + )( + "should throw notValidVersionState if the purpose version is in %s state", + async (state) => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(state); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + }) + ).rejects.toThrowError( + notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) + ); + } + ); + }); From b9615740eae4f676991a690963ed8684e4b04393 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 11:00:15 +0200 Subject: [PATCH 287/537] Split test file --- .../purpose-process/test/testGetPurposes.ts | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 packages/purpose-process/test/testGetPurposes.ts diff --git a/packages/purpose-process/test/testGetPurposes.ts b/packages/purpose-process/test/testGetPurposes.ts new file mode 100644 index 0000000000..6005cc3cfc --- /dev/null +++ b/packages/purpose-process/test/testGetPurposes.ts @@ -0,0 +1,346 @@ +import { + EService, + Purpose, + PurposeVersion, + TenantId, + generateId, + purposeVersionState, + toReadModelEService, +} from "pagopa-interop-models"; +import { beforeEach, describe, expect, it } from "vitest"; +import { + getMockPurposeVersion, + getMockPurpose, + writeInReadmodel, +} from "pagopa-interop-commons-test/index.js"; +import { addOnePurpose, getMockEService } from "./utils.js"; +import { + postgresDB, + purposes, + eservices, + purposeService, +} from "./purposeService.integration.test.js"; + +export const testGetPurposes = (): ReturnType => + describe("getPurposes", async () => { + const producerId1: TenantId = generateId(); + const producerId2: TenantId = generateId(); + const consumerId1: TenantId = generateId(); + + const mockEService1ByTenant1: EService = { + ...getMockEService(), + producerId: producerId1, + }; + + const mockEService2ByTenant1: EService = { + ...getMockEService(), + producerId: producerId1, + }; + + const mockEService3ByTenant2: EService = { + ...getMockEService(), + producerId: producerId2, + }; + + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + title: "purpose 1 - test", + consumerId: consumerId1, + eserviceId: mockEService1ByTenant1.id, + versions: [mockPurposeVersion1], + }; + + const mockPurposeVersion2: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose2: Purpose = { + ...getMockPurpose(), + title: "purpose 2", + eserviceId: mockEService1ByTenant1.id, + versions: [mockPurposeVersion2], + }; + + const mockPurposeVersion3: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose3: Purpose = { + ...getMockPurpose(), + title: "purpose 3", + eserviceId: mockEService2ByTenant1.id, + versions: [mockPurposeVersion3], + }; + + const mockPurposeVersion4: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose4: Purpose = { + ...getMockPurpose(), + title: "purpose 4", + eserviceId: mockEService3ByTenant2.id, + versions: [mockPurposeVersion4], + }; + + const mockPurposeVersion5: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.rejected, + }; + const mockPurpose5: Purpose = { + ...getMockPurpose(), + title: "purpose 5", + consumerId: consumerId1, + versions: [mockPurposeVersion5], + }; + + const mockPurposeVersion6_1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.archived, + }; + const mockPurposeVersion6_2: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.draft, + }; + const mockPurpose6: Purpose = { + ...getMockPurpose(), + title: "purpose 6 - test", + consumerId: consumerId1, + eserviceId: mockEService3ByTenant2.id, + versions: [mockPurposeVersion6_1, mockPurposeVersion6_2], + }; + + const mockPurpose7: Purpose = { + ...getMockPurpose(), + title: "purpose 7 - test", + versions: [], + }; + + beforeEach(async () => { + await addOnePurpose(mockPurpose1, postgresDB, purposes); + await addOnePurpose(mockPurpose2, postgresDB, purposes); + await addOnePurpose(mockPurpose3, postgresDB, purposes); + await addOnePurpose(mockPurpose4, postgresDB, purposes); + await addOnePurpose(mockPurpose5, postgresDB, purposes); + await addOnePurpose(mockPurpose6, postgresDB, purposes); + await addOnePurpose(mockPurpose7, postgresDB, purposes); + + await writeInReadmodel( + toReadModelEService(mockEService1ByTenant1), + eservices + ); + await writeInReadmodel( + toReadModelEService(mockEService2ByTenant1), + eservices + ); + await writeInReadmodel( + toReadModelEService(mockEService3ByTenant2), + eservices + ); + }); + + it("should get the purposes if they exist (parameters: name)", async () => { + const result = await purposeService.getPurposes( + { + name: "test", + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose6, + mockPurpose7, + ]); + }); + it("should get the purposes if they exist (parameters: eservicesIds)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [mockEService1ByTenant1.id], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([mockPurpose1, mockPurpose2]); + }); + it("should get the purposes if they exist (parameters: consumersIds)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [consumerId1], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose5, + mockPurpose6, + ]); + }); + it("should get the purposes if they exist (parameters: producersIds)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [producerId2], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([mockPurpose4, mockPurpose6]); + }); + it("should get the purposes if they exist (parameters: states)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [purposeVersionState.rejected, purposeVersionState.archived], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + mockPurpose4, + mockPurpose5, + mockPurpose6, + ]); + }); + it("should not include draft versions and purposes without versions (excludeDraft = true)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: true, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(4); + expect(result.results).toEqual([ + mockPurpose3, + mockPurpose4, + mockPurpose5, + { ...mockPurpose6, versions: [mockPurposeVersion6_1] }, + ]); + }); + it("should include draft versions and purposes without versions (excludeDraft = false)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose2, + mockPurpose3, + mockPurpose4, + mockPurpose5, + mockPurpose6, + mockPurpose7, + ]); + }); + it("should get the purposes if they exist (pagination: offset)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 5, limit: 50 } + ); + expect(result.results).toEqual([mockPurpose6, mockPurpose7]); + }); + it("should get the purposes if they exist (pagination: limit)", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 3 } + ); + expect(result.results).toEqual([ + mockPurpose1, + mockPurpose2, + mockPurpose3, + ]); + }); + it("should not get the purposes if they don't exist", async () => { + const result = await purposeService.getPurposes( + { + eservicesIds: [generateId()], + consumersIds: [], + producersIds: [generateId()], + states: [], + excludeDraft: undefined, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(0); + expect(result.results).toEqual([]); + }); + it("should get the purposes if they exist (parameters: name, eservicesIds, consumersIds, producersIds, states; exlcudeDraft = true)", async () => { + const result = await purposeService.getPurposes( + { + name: "test", + eservicesIds: [mockEService3ByTenant2.id], + consumersIds: [consumerId1], + producersIds: [producerId2], + states: [purposeVersionState.archived], + excludeDraft: true, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(1); + expect(result.results).toEqual([ + { ...mockPurpose6, versions: [mockPurposeVersion6_1] }, + ]); + }); + it("should get the purposes if they exist (parameters: name, eservicesIds, consumersIds, producersIds, states; exlcudeDraft = false)", async () => { + const result = await purposeService.getPurposes( + { + name: "test", + eservicesIds: [mockEService1ByTenant1.id], + consumersIds: [consumerId1], + producersIds: [producerId1], + states: [purposeVersionState.draft], + excludeDraft: false, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(1); + expect(result.results).toEqual([mockPurpose1]); + }); + }); From 9a90f1c07b5a18e287ae08b601d9c5f5e97086cd Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Mon, 22 Apr 2024 16:14:49 +0200 Subject: [PATCH 288/537] added createPurpose route --- packages/models/src/purpose/purpose.ts | 11 -- .../src/model/domain/errors.ts | 21 ++ .../src/model/domain/toEvent.ts | 17 ++ .../src/routers/PurposeRouter.ts | 23 ++- .../src/services/purposeService.ts | 71 +++++-- .../src/services/readModelService.ts | 183 ++++-------------- .../src/utilities/errorMappers.ts | 11 ++ 7 files changed, 161 insertions(+), 176 deletions(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 26944ba510..feecc4e13c 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -61,17 +61,6 @@ export const Purpose = z.object({ }); export type Purpose = z.infer; -export const PurposeSeed = z.object({ - eserviceId: EServiceId, - consumerId: TenantId, - riskAnalysisForm: PurposeRiskAnalysisForm.optional(), - title: z.string(), - description: z.string(), - isFreeOfCharge: z.boolean(), - freeOfChargeReason: z.string().optional(), -}); -export type PurposeSeed = z.infer; - export const ownership = { CONSUMER: "CONSUMER", PRODUCER: "PRODUCER", diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a76da0a02b..1e4beae644 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -28,6 +28,8 @@ export const errorCodes = { purposeNotInDraftState: "0014", notValidVersionState: "0015", purposeCannotBeDeleted: "0016", + agreementNotFound: "0017", + duplicatedPurposeName: "0018", }; export type ErrorCodes = keyof typeof errorCodes; @@ -180,6 +182,25 @@ export function notValidVersionState( }); } +export function agreementNotFound( + eserviceId: EServiceId, + consumerId: TenantId +): ApiError { + return new ApiError({ + detail: `No Agreement found for EService ${eserviceId} and Consumer ${consumerId}`, + code: "agreementNotFound", + title: "Agreement Not Found", + }); +} + +export function duplicatedPurposeName(title: string): ApiError { + return new ApiError({ + detail: `Purpose with name: ${title} already in use`, + code: "duplicatedPurposeName", + title: "Duplicated Purpose Name", + }); +} + export function purposeCannotBeDeleted( purposeId: PurposeId ): ApiError { diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index e7ef1b96de..f909edcee1 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -69,6 +69,23 @@ export const toCreateEventDraftPurposeUpdated = ({ correlationId, }); +export function toCreateEventPurposeAdded( + purpose: Purpose, + correlationId: string +): CreateEvent { + return { + streamId: purpose.id, + version: 0, + event: { + type: "PurposeAdded", + event_version: 2, + data: { + purpose: toPurposeV2(purpose), + }, + }, + correlationId, + }; +} export const toCreateEventDraftPurposeDeleted = ({ purpose, version, diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 8e131438d0..2780a8994d 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -22,6 +22,7 @@ import { purposeServiceBuilder } from "../services/purposeService.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { archivePurposeVersionErrorMapper, + createPurposeErrorMapper, deletePurposeErrorMapper, deletePurposeVersionErrorMapper, getPurposeErrorMapper, @@ -109,8 +110,26 @@ const purposeRouter = ( } } ) - .post("/purposes", authorizationMiddleware([ADMIN_ROLE]), (_req, res) => - res.status(501).send() + .post( + "/purposes", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.createPurpose( + req.body, + req.ctx.authData.organizationId, + req.ctx.correlationId + ); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, createPurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/reverse/purposes", diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 2d121de2a2..de50adee8f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -25,12 +25,14 @@ import { PurposeEvent, EServiceMode, ListResult, - PurposeSeed, + unsafeBrandId, + generateId, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { + agreementNotFound, + duplicatedPurposeName, eserviceNotFound, - missingFreeOfChargeReason, notValidVersionState, organizationIsNotTheConsumer, organizationIsNotTheProducer, @@ -45,6 +47,7 @@ import { import { toCreateEventDraftPurposeDeleted, toCreateEventDraftPurposeUpdated, + toCreateEventPurposeAdded, toCreateEventPurposeArchived, toCreateEventPurposeSuspendedByConsumer, toCreateEventPurposeSuspendedByProducer, @@ -56,8 +59,9 @@ import { ApiPurposeUpdateContent, ApiReversePurposeUpdateContent, ApiGetPurposesFilters, + ApiPurposeSeed, } from "../model/domain/models.js"; -import { AgreementQueryFilters, ReadModelService } from "./readModelService.js"; +import { ReadModelService } from "./readModelService.js"; import { assertOrganizationIsAConsumer, assertEserviceHasSpecificMode, @@ -526,31 +530,68 @@ export function purposeServiceBuilder( }; }, async createPurpose( - purposeSeed: PurposeSeed, - organizationId: TenantId - ): Promise { + purposeSeed: ApiPurposeSeed, + organizationId: TenantId, + correlationId: string + ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info( `Creating Purpose for EService ${purposeSeed.eserviceId} and Consumer ${purposeSeed.consumerId}` ); - assertOrganizationIsAConsumer(organizationId, purposeSeed.consumerId); + const eserviceId = unsafeBrandId(purposeSeed.eserviceId); + const consumerId = unsafeBrandId(purposeSeed.consumerId); + assertOrganizationIsAConsumer(organizationId, consumerId); + + assertConsistentFreeOfCharge( + purposeSeed.isFreeOfCharge, + purposeSeed.freeOfChargeReason + ); - if (purposeSeed.isFreeOfCharge && !purposeSeed.freeOfChargeReason) { - throw missingFreeOfChargeReason(); - } const tenant = await retrieveTenant(organizationId, readModelService); if (!tenant.kind) { throw tenantKindNotFound(tenant.id); } - const clientSeed; + const validatedFormSeed = validateAndTransformRiskAnalysis( + purposeSeed.riskAnalysisForm, + tenant.kind + ); - const filters: AgreementQueryFilters; + const agreement = await readModelService.getActiveAgreement( + eserviceId, + consumerId + ); - const agreementsx = await readModelService.getAllAgreements( - agreements, - filters + if (agreement === undefined) { + throw agreementNotFound(eserviceId, consumerId); + } + + const purposeWithSameName = await readModelService.getSpecificPurpose( + eserviceId, + consumerId, + purposeSeed.title ); + + if (purposeWithSameName) { + throw duplicatedPurposeName(purposeSeed.title); + } + + const purpose: Purpose = { + title: purposeSeed.title, + id: generateId(), + createdAt: new Date(), + eserviceId, + consumerId, + description: purposeSeed.description, + versions: [], + isFreeOfCharge: purposeSeed.isFreeOfCharge, + freeOfChargeReason: purposeSeed.freeOfChargeReason, + riskAnalysisForm: validatedFormSeed, + }; + + const event = toCreateEventPurposeAdded(purpose, correlationId); + await repository.createEvent(event); + return { purpose, isRiskAnalysisValid: validatedFormSeed !== undefined }; }, }; } diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 1f86e2a283..26005299e8 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -5,9 +5,6 @@ import { TenantCollection, PurposeCollection, ReadModelFilter, - AgreementCollection, - MongoQueryKeys, - RemoveDataPrefix, } from "pagopa-interop-commons"; import { EService, @@ -22,152 +19,12 @@ import { ListResult, purposeVersionState, Agreement, - AgreementState, - AttributeId, - DescriptorId, agreementState, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; -import { match, P } from "ts-pattern"; import { ApiGetPurposesFilters } from "../model/domain/models.js"; -export type AgreementQueryFilters = { - producerId?: string | string[]; - consumerId?: string | string[]; - eserviceId?: EServiceId | EServiceId[]; - descriptorId?: DescriptorId | DescriptorId[]; - agreementStates?: AgreementState[]; - attributeId?: AttributeId | AttributeId[]; - showOnlyUpgradeable?: boolean; -}; - -type AgreementDataFields = RemoveDataPrefix>; - -const makeFilter = ( - fieldName: Extract< - AgreementDataFields, - "producerId" | "consumerId" | "eserviceId" | "descriptorId" - >, - value: string | string[] | undefined -): ReadModelFilter | undefined => - match(value) - .with(P.nullish, () => undefined) - .with(P.string, () => ({ - [`data.${fieldName}`]: value, - })) - .with(P.array(P.string), (a) => - a.length === 0 ? undefined : { [`data.${fieldName}`]: { $in: value } } - ) - .exhaustive(); - -const makeAttributesFilter = ( - fieldName: Extract< - AgreementDataFields, - "certifiedAttributes" | "declaredAttributes" | "verifiedAttributes" - >, - attributeIds: AttributeId | AttributeId[] -): ReadModelFilter | undefined => - match(attributeIds) - .with(P.string, (id) => ({ - [`data.${fieldName}`]: { $elemMatch: { id } }, - })) - .with(P.array(P.string), (ids) => - ids.length === 0 - ? undefined - : { - [`data.${fieldName}`]: { - $elemMatch: { id: { $in: ids } }, - }, - } - ) - .exhaustive(); - -const getAgreementsFilters = ( - filters: AgreementQueryFilters -): { $match: object } => { - const upgradeableStates = [ - agreementState.draft, - agreementState.active, - agreementState.suspended, - ]; - - const { - attributeId, - producerId, - consumerId, - eserviceId, - descriptorId, - agreementStates, - showOnlyUpgradeable, - } = filters; - - const agreementStatesFilters = match(agreementStates) - .with(P.nullish, () => (showOnlyUpgradeable ? upgradeableStates : [])) - .with( - P.when( - (agreementStates) => agreementStates.length === 0 && showOnlyUpgradeable - ), - () => upgradeableStates - ) - .with( - P.when( - (agreementStates) => agreementStates.length > 0 && showOnlyUpgradeable - ), - (agreementStates) => - upgradeableStates.filter((s) => agreementStates.includes(s)) - ) - .otherwise((agreementStates) => agreementStates); - - const queryFilters = { - ...makeFilter("producerId", producerId), - ...makeFilter("consumerId", consumerId), - ...makeFilter("eserviceId", eserviceId), - ...makeFilter("descriptorId", descriptorId), - ...(agreementStatesFilters && - agreementStatesFilters.length > 0 && { - "data.state": { - $in: agreementStatesFilters.map((s) => s.toString()), - }, - }), - ...(attributeId && { - $or: [ - makeAttributesFilter("certifiedAttributes", attributeId), - makeAttributesFilter("verifiedAttributes", attributeId), - makeAttributesFilter("declaredAttributes", attributeId), - ], - }), - }; - return { $match: queryFilters }; -}; - -const getAllAgreementsConst = async ( - agreements: AgreementCollection, - filters: AgreementQueryFilters -): Promise => { - const data = await agreements - .aggregate([getAgreementsFilters(filters)]) - .toArray(); - - const result = z - .array( - z.object({ - data: Agreement, - }) - ) - .safeParse(data); - - if (!result.success) { - logger.error( - `Unable to parse agreements items: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - throw genericError("Unable to parse agreements items"); - } - return result.data.map((d) => d.data); -}; - async function getPurpose( purposes: PurposeCollection, filter: Filter>> @@ -260,11 +117,41 @@ export function readModelServiceBuilder( ): Promise | undefined> { return getPurpose(purposes, { "data.id": id }); }, - async getAllAgreements( - agreements: AgreementCollection, - filters: AgreementQueryFilters - ): Promise { - return getAllAgreementsConst(agreements, filters); + async getSpecificPurpose( + eserviceId: EServiceId, + consumerId: TenantId, + title: string + ): Promise | undefined> { + return getPurpose(purposes, { + "data.eserviceId": eserviceId, + "data.consumerId": consumerId, + "data.title": { + $regex: `^${ReadModelRepository.escapeRegExp(title)}$$`, + $options: "i", + }, + } satisfies ReadModelFilter); + }, + async getActiveAgreement( + eserviceId: EServiceId, + consumerId: TenantId + ): Promise { + const data = await agreements.findOne({ + "data.eserviceId": eserviceId, + "data.consumerId": consumerId, + "data.state": agreementState.active, + }); + + const result = Agreement.safeParse(data); + if (!result.success) { + logger.error( + `Unable to parse agreements items: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + + throw genericError("Unable to parse agreements items"); + } + return result.data; }, async getPurposes( filters: ApiGetPurposesFilters, diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 06c19aa357..da5f1a4259 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -21,6 +21,17 @@ export const getPurposeErrorMapper = (error: ApiError): number => .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); +export const createPurposeErrorMapper = (error: ApiError): number => + match(error.code) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with("missingFreeOfChargeReason", () => HTTP_STATUS_NOT_FOUND) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("riskAnalysisValidationFailed", () => HTTP_STATUS_FORBIDDEN) + .with("agreementNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("duplicatedPurposeName", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + export const getRiskAnalysisDocumentErrorMapper = ( error: ApiError ): number => From a5ccd0b1d807fae6a208bc8c643fda179bcd1eb0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:15:27 +0200 Subject: [PATCH 289/537] Add allowDiskUse --- .../purpose-process/src/services/readModelService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 992f20cad5..85c9bc5c4f 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -211,11 +211,10 @@ export function readModelServiceBuilder( ]; const data = await purposes - .aggregate([ - ...aggregationPipeline, - { $skip: offset }, - { $limit: limit }, - ]) + .aggregate( + [...aggregationPipeline, { $skip: offset }, { $limit: limit }], + { allowDiskUse: true } + ) .toArray(); const result = z.array(Purpose).safeParse(data.map((d) => d.data)); From 0241a9c2715d4583cf76e3b123be04d49bafe6d6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:15:34 +0200 Subject: [PATCH 290/537] Add check --- .../src/routers/PurposeRouter.ts | 1 + .../src/services/purposeService.ts | 43 +++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 8e131438d0..6bcc704c69 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -84,6 +84,7 @@ const purposeRouter = ( limit, } = req.query; const purposes = await purposeService.getPurposes( + req.ctx.authData.organizationId, { name, eservicesIds: eservicesIds.map(unsafeBrandId), diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 19579efc40..1d67b483a7 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -495,6 +495,7 @@ export function purposeServiceBuilder( return suspendedPurposeVersion; }, async getPurposes( + organizationId: TenantId, filters: ApiGetPurposesFilters, { offset, limit }: { offset: number; limit: number } ): Promise> { @@ -508,18 +509,42 @@ export function purposeServiceBuilder( limit ); - const purposesToReturn: Purpose[] = purposesList.results.map( - (purpose) => ({ - ...purpose, - versions: filters.excludeDraft - ? purpose.versions.filter( - (version) => version.state !== purposeVersionState.draft - ) - : purpose.versions, - riskAnalysisForm: undefined, + const mappingPurposeEservice = await Promise.all( + purposesList.results.map(async (purpose) => { + const eservice = await retrieveEService( + purpose.eserviceId, + readModelService + ); + if (eservice === undefined) { + throw eserviceNotFound(purpose.eserviceId); + } + return { + purpose, + eservice, + }; }) ); + const purposesToReturn = mappingPurposeEservice.map( + ({ purpose, eservice }) => { + const isProducerOrConsumer = + organizationId === purpose.consumerId || + organizationId === eservice.producerId; + + return { + ...purpose, + versions: filters.excludeDraft + ? purpose.versions.filter( + (version) => version.state !== purposeVersionState.draft + ) + : purpose.versions, + riskAnalysisForm: isProducerOrConsumer + ? purpose.riskAnalysisForm + : undefined, + }; + } + ); + return { results: purposesToReturn, totalCount: purposesList.totalCount, From dfb835bf10dc1b105541ff74a71dd2fc63a793ed Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:16:51 +0200 Subject: [PATCH 291/537] Fix import --- packages/purpose-process/test/testGetPurposeById.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testGetPurposeById.ts b/packages/purpose-process/test/testGetPurposeById.ts index 48c5dfac2e..a52876ce59 100644 --- a/packages/purpose-process/test/testGetPurposeById.ts +++ b/packages/purpose-process/test/testGetPurposeById.ts @@ -3,7 +3,7 @@ import { getMockTenant, getMockPurpose, writeInReadmodel, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { tenantKind, Purpose, From 3290ef96f3c7bb70fffac5eb21ae19ac9d404e11 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:17:27 +0200 Subject: [PATCH 292/537] Fix import --- packages/purpose-process/test/testGetRiskAnalysisDocument.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts index bf2c754105..e258c3d6bd 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts @@ -4,7 +4,7 @@ import { getMockPurposeVersion, getMockPurpose, writeInReadmodel, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { Purpose, generateId, From aafd684fbbd360fa207089ee549a5dd0222008b6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:18:05 +0200 Subject: [PATCH 293/537] Fix import --- packages/purpose-process/test/testDeletePurposeVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index 9ec8f3de55..b91e8ed35f 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -6,7 +6,7 @@ import { writeInReadmodel, readLastEventByStreamId, decodeProtobufPayload, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { purposeVersionState, Purpose, From 5c7842250aa9a96f9f0b20235dd99da490303b1e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:18:39 +0200 Subject: [PATCH 294/537] Fix import --- packages/purpose-process/test/testRejectPurposeVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testRejectPurposeVersion.ts b/packages/purpose-process/test/testRejectPurposeVersion.ts index 3b01b09c65..cb48b9809c 100644 --- a/packages/purpose-process/test/testRejectPurposeVersion.ts +++ b/packages/purpose-process/test/testRejectPurposeVersion.ts @@ -5,7 +5,7 @@ import { writeInReadmodel, readLastEventByStreamId, decodeProtobufPayload, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { purposeVersionState, Purpose, From 383aee90c6bb12ce3d24adbd8cf05db92115e350 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:19:13 +0200 Subject: [PATCH 295/537] Fix import --- packages/purpose-process/test/testDeletePurpose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testDeletePurpose.ts b/packages/purpose-process/test/testDeletePurpose.ts index d6ed825a75..61643cd4a9 100644 --- a/packages/purpose-process/test/testDeletePurpose.ts +++ b/packages/purpose-process/test/testDeletePurpose.ts @@ -17,7 +17,7 @@ import { readLastEventByStreamId, decodeProtobufPayload, getMockPurposeVersion, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { purposeNotFound, organizationIsNotTheConsumer, From 92964c1edb84a1604fd07ccb069ee6803f0b5602 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:19:33 +0200 Subject: [PATCH 296/537] Fix import --- packages/purpose-process/test/testArchivePurposeVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testArchivePurposeVersion.ts b/packages/purpose-process/test/testArchivePurposeVersion.ts index 93426246e6..c074e381d8 100644 --- a/packages/purpose-process/test/testArchivePurposeVersion.ts +++ b/packages/purpose-process/test/testArchivePurposeVersion.ts @@ -4,7 +4,7 @@ import { getMockPurpose, readLastEventByStreamId, decodeProtobufPayload, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { PurposeVersion, purposeVersionState, From 2bf8719590085a599920eaf7cf0658610d624a1f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:19:51 +0200 Subject: [PATCH 297/537] Fix import --- packages/purpose-process/test/testSuspendPurposeVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testSuspendPurposeVersion.ts b/packages/purpose-process/test/testSuspendPurposeVersion.ts index 3c8f1743d7..bb6a1e37e9 100644 --- a/packages/purpose-process/test/testSuspendPurposeVersion.ts +++ b/packages/purpose-process/test/testSuspendPurposeVersion.ts @@ -6,7 +6,7 @@ import { writeInReadmodel, readLastEventByStreamId, decodeProtobufPayload, -} from "pagopa-interop-commons-test/index.js"; +} from "pagopa-interop-commons-test"; import { PurposeVersion, purposeVersionState, From ffdd29bea7cfefc9187329862dc7c272b9103656 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 22 Apr 2024 17:55:37 +0200 Subject: [PATCH 298/537] Fix and improve tests --- .../purpose-process/test/testGetPurposes.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/purpose-process/test/testGetPurposes.ts b/packages/purpose-process/test/testGetPurposes.ts index 6005cc3cfc..646a320902 100644 --- a/packages/purpose-process/test/testGetPurposes.ts +++ b/packages/purpose-process/test/testGetPurposes.ts @@ -42,6 +42,8 @@ export const testGetPurposes = (): ReturnType => producerId: producerId2, }; + const randomEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { ...getMockPurposeVersion(), state: purposeVersionState.draft, @@ -95,6 +97,7 @@ export const testGetPurposes = (): ReturnType => ...getMockPurpose(), title: "purpose 5", consumerId: consumerId1, + eserviceId: randomEService.id, versions: [mockPurposeVersion5], }; @@ -118,6 +121,7 @@ export const testGetPurposes = (): ReturnType => ...getMockPurpose(), title: "purpose 7 - test", versions: [], + eserviceId: randomEService.id, }; beforeEach(async () => { @@ -141,10 +145,12 @@ export const testGetPurposes = (): ReturnType => toReadModelEService(mockEService3ByTenant2), eservices ); + await writeInReadmodel(toReadModelEService(randomEService), eservices); }); it("should get the purposes if they exist (parameters: name)", async () => { const result = await purposeService.getPurposes( + producerId1, { name: "test", eservicesIds: [], @@ -164,6 +170,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (parameters: eservicesIds)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [mockEService1ByTenant1.id], consumersIds: [], @@ -178,6 +185,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (parameters: consumersIds)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [consumerId1], @@ -196,6 +204,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (parameters: producersIds)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [], @@ -210,6 +219,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (parameters: states)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [], @@ -228,6 +238,7 @@ export const testGetPurposes = (): ReturnType => }); it("should not include draft versions and purposes without versions (excludeDraft = true)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [], @@ -247,6 +258,7 @@ export const testGetPurposes = (): ReturnType => }); it("should include draft versions and purposes without versions (excludeDraft = false)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [], @@ -269,6 +281,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (pagination: offset)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [], @@ -282,6 +295,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (pagination: limit)", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [], consumersIds: [], @@ -299,6 +313,7 @@ export const testGetPurposes = (): ReturnType => }); it("should not get the purposes if they don't exist", async () => { const result = await purposeService.getPurposes( + producerId1, { eservicesIds: [generateId()], consumersIds: [], @@ -313,6 +328,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (parameters: name, eservicesIds, consumersIds, producersIds, states; exlcudeDraft = true)", async () => { const result = await purposeService.getPurposes( + producerId1, { name: "test", eservicesIds: [mockEService3ByTenant2.id], @@ -330,6 +346,7 @@ export const testGetPurposes = (): ReturnType => }); it("should get the purposes if they exist (parameters: name, eservicesIds, consumersIds, producersIds, states; exlcudeDraft = false)", async () => { const result = await purposeService.getPurposes( + producerId1, { name: "test", eservicesIds: [mockEService1ByTenant1.id], @@ -343,4 +360,29 @@ export const testGetPurposes = (): ReturnType => expect(result.totalCount).toBe(1); expect(result.results).toEqual([mockPurpose1]); }); + it("should not inlcude the riskAnalysisForm uf the requester is not the producer nor the consumer", async () => { + const result = await purposeService.getPurposes( + generateId(), + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual( + [ + mockPurpose1, + mockPurpose2, + mockPurpose3, + mockPurpose4, + mockPurpose5, + mockPurpose6, + mockPurpose7, + ].map((p) => ({ ...p, riskAnalysisForm: undefined })) + ); + }); }); From 105d6f1e509d83d541ddac9bf45a6a01b79184e9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 09:19:43 +0200 Subject: [PATCH 299/537] Improve test --- .../purpose-process/test/testGetPurposes.ts | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/test/testGetPurposes.ts b/packages/purpose-process/test/testGetPurposes.ts index 646a320902..7b25b9f480 100644 --- a/packages/purpose-process/test/testGetPurposes.ts +++ b/packages/purpose-process/test/testGetPurposes.ts @@ -5,6 +5,7 @@ import { TenantId, generateId, purposeVersionState, + tenantKind, toReadModelEService, } from "pagopa-interop-models"; import { beforeEach, describe, expect, it } from "vitest"; @@ -12,6 +13,7 @@ import { getMockPurposeVersion, getMockPurpose, writeInReadmodel, + getMockValidRiskAnalysisForm, } from "pagopa-interop-commons-test/index.js"; import { addOnePurpose, getMockEService } from "./utils.js"; import { @@ -19,6 +21,7 @@ import { purposes, eservices, purposeService, + readModelService, } from "./purposeService.integration.test.js"; export const testGetPurposes = (): ReturnType => @@ -42,7 +45,7 @@ export const testGetPurposes = (): ReturnType => producerId: producerId2, }; - const randomEService = getMockEService(); + const mockEService4 = getMockEService(); const mockPurposeVersion1: PurposeVersion = { ...getMockPurposeVersion(), @@ -97,7 +100,7 @@ export const testGetPurposes = (): ReturnType => ...getMockPurpose(), title: "purpose 5", consumerId: consumerId1, - eserviceId: randomEService.id, + eserviceId: mockEService4.id, versions: [mockPurposeVersion5], }; @@ -121,7 +124,7 @@ export const testGetPurposes = (): ReturnType => ...getMockPurpose(), title: "purpose 7 - test", versions: [], - eserviceId: randomEService.id, + eserviceId: mockEService4.id, }; beforeEach(async () => { @@ -145,7 +148,7 @@ export const testGetPurposes = (): ReturnType => toReadModelEService(mockEService3ByTenant2), eservices ); - await writeInReadmodel(toReadModelEService(randomEService), eservices); + await writeInReadmodel(toReadModelEService(mockEService4), eservices); }); it("should get the purposes if they exist (parameters: name)", async () => { @@ -360,7 +363,14 @@ export const testGetPurposes = (): ReturnType => expect(result.totalCount).toBe(1); expect(result.results).toEqual([mockPurpose1]); }); - it("should not inlcude the riskAnalysisForm uf the requester is not the producer nor the consumer", async () => { + it("should not include the riskAnalysisForm if the requester is not the producer nor the consumer", async () => { + const mockPurpose8: Purpose = { + ...getMockPurpose(), + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + eserviceId: mockEService4.id, + }; + await addOnePurpose(mockPurpose8, postgresDB, purposes); + const result = await purposeService.getPurposes( generateId(), { @@ -372,7 +382,7 @@ export const testGetPurposes = (): ReturnType => }, { offset: 0, limit: 50 } ); - expect(result.totalCount).toBe(7); + expect(result.totalCount).toBe(8); expect(result.results).toEqual( [ mockPurpose1, @@ -382,7 +392,42 @@ export const testGetPurposes = (): ReturnType => mockPurpose5, mockPurpose6, mockPurpose7, + mockPurpose8, ].map((p) => ({ ...p, riskAnalysisForm: undefined })) ); }); + it("should only include the riskAnalysisForm for those purposes in which the requester is the producer or the consumer", async () => { + await purposes.deleteMany({}); + + const mockPurpose8: Purpose = { + ...getMockPurpose(), + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + eserviceId: mockEService2ByTenant1.id, + }; + await addOnePurpose(mockPurpose8, postgresDB, purposes); + + const mockPurpose9: Purpose = { + ...getMockPurpose(), + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + eserviceId: mockEService4.id, + }; + await addOnePurpose(mockPurpose9, postgresDB, purposes); + + const result = await purposeService.getPurposes( + producerId1, + { + eservicesIds: [], + consumersIds: [], + producersIds: [], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 } + ); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([ + mockPurpose8, + { ...mockPurpose9, riskAnalysisForm: undefined }, + ]); + }); }); From 6d6cf30c1a6829474010adae6dab4a9f4a8a635c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 09:34:15 +0200 Subject: [PATCH 300/537] Remove unused import --- packages/purpose-process/test/testGetPurposes.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/test/testGetPurposes.ts b/packages/purpose-process/test/testGetPurposes.ts index 7b25b9f480..bc10d9614a 100644 --- a/packages/purpose-process/test/testGetPurposes.ts +++ b/packages/purpose-process/test/testGetPurposes.ts @@ -21,7 +21,6 @@ import { purposes, eservices, purposeService, - readModelService, } from "./purposeService.integration.test.js"; export const testGetPurposes = (): ReturnType => From 0f6123eb9d6ce9ac3d0098a0dcb65edcb2d9372e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 09:36:17 +0200 Subject: [PATCH 301/537] Update error mapper --- packages/purpose-process/src/routers/PurposeRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 6bcc704c69..5f44fbd41b 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -105,7 +105,7 @@ const purposeRouter = ( }) .end(); } catch (error) { - const errorRes = makeApiProblem(error, getPurposeErrorMapper); + const errorRes = makeApiProblem(error, () => 500); return res.status(errorRes.status).json(errorRes).end(); } } From 5ecf50396a7fb75a9416b0c2852e08767218e511 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 10:55:29 +0200 Subject: [PATCH 302/537] Add check on rejection reason --- .../src/model/domain/errors.ts | 9 +++++++ .../src/services/purposeService.ts | 6 +++++ .../test/testRejectPurposeVersion.ts | 26 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 955c186a10..dfb60472a2 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -22,6 +22,7 @@ export const errorCodes = { purposeVersionCannotBeDeleted: "0009", organizationIsNotTheProducer: "0010", notValidVersionState: "0011", + missingRejectionReason: "0012", }; export type ErrorCodes = keyof typeof errorCodes; @@ -134,3 +135,11 @@ export function notValidVersionState( title: "Not valid purpose version state", }); } + +export function missingRejectionReason(): ApiError { + return new ApiError({ + detail: `Rejection reason is missing`, + code: "missingRejectionReason", + title: "Rejection reason can't be omitted", + }); +} diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index c40a4f9f7e..b587d97506 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -19,6 +19,7 @@ import { } from "pagopa-interop-models"; import { eserviceNotFound, + missingRejectionReason, notValidVersionState, organizationIsNotTheConsumer, organizationIsNotTheProducer, @@ -237,6 +238,11 @@ export function purposeServiceBuilder( if (purposeVersion.state !== purposeVersionState.waitingForApproval) { throw notValidVersionState(purposeVersion.id, purposeVersion.state); } + + if (!rejectionReason) { + throw missingRejectionReason(); + } + const updatedPurposeVersion: PurposeVersion = { ...purposeVersion, state: purposeVersionState.rejected, diff --git a/packages/purpose-process/test/testRejectPurposeVersion.ts b/packages/purpose-process/test/testRejectPurposeVersion.ts index cb48b9809c..ba09c1ccc1 100644 --- a/packages/purpose-process/test/testRejectPurposeVersion.ts +++ b/packages/purpose-process/test/testRejectPurposeVersion.ts @@ -24,6 +24,7 @@ import { organizationIsNotTheProducer, purposeVersionNotFound, notValidVersionState, + missingRejectionReason, } from "../src/model/domain/errors.js"; import { postgresDB, @@ -220,4 +221,29 @@ export const testRejectPurposeVersion = (): ReturnType => ); } ); + it("should throw missingRejectionReason if the rejection reason has been omitted", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.waitingForApproval, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "", + organizationId: mockEService.producerId, + correlationId: generateId(), + }) + ).rejects.toThrowError(missingRejectionReason()); + }); }); From 50f367e1799402a66b5851ec59e791d39756a49e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 10:56:46 +0200 Subject: [PATCH 303/537] Add to do --- packages/purpose-process/src/routers/PurposeRouter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 2780a8994d..3f9ee6a952 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -134,6 +134,7 @@ const purposeRouter = ( .post( "/reverse/purposes", authorizationMiddleware([ADMIN_ROLE]), + // TO DO (_req, res) => res.status(501).send() ) .post( From d04c42f3439e8d414af74edcc8939ba6c2971599 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 12:30:33 +0200 Subject: [PATCH 304/537] Add type for seed --- packages/purpose-process/src/model/domain/models.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 63b84aaac4..0bd9f1310f 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -38,3 +38,7 @@ export type ApiGetPurposesFilters = { states: PurposeVersionState[]; excludeDraft: boolean | undefined; }; + +export type ApiReversePurposeSeed = z.infer< + typeof api.schemas.EServicePurposeSeed +>; From 4b794baca2bc7aada114ab7c877c8d7446fc7742 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 12:32:06 +0200 Subject: [PATCH 305/537] Add error --- packages/purpose-process/src/model/domain/errors.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 1e4beae644..e5606c873e 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -6,6 +6,7 @@ import { PurposeVersionDocumentId, PurposeVersionId, PurposeVersionState, + RiskAnalysisId, TenantId, makeApiProblemBuilder, } from "pagopa-interop-models"; @@ -30,6 +31,7 @@ export const errorCodes = { purposeCannotBeDeleted: "0016", agreementNotFound: "0017", duplicatedPurposeName: "0018", + eserviceRiskAnalysisNotFound: "0019", }; export type ErrorCodes = keyof typeof errorCodes; @@ -210,3 +212,14 @@ export function purposeCannotBeDeleted( title: "Purpose canont be deleted", }); } + +export function eserviceRiskAnalysisNotFound( + eserviceId: EServiceId, + riskAnalysisId: RiskAnalysisId +): ApiError { + return new ApiError({ + detail: `Risk Analysis ${riskAnalysisId} not found for EService ${eserviceId}`, + code: "eserviceRiskAnalysisNotFound", + title: "Risk analysis not found", + }); +} From 86556afe39a313ad1296c4ed9198edf82703fce5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 12:32:12 +0200 Subject: [PATCH 306/537] Add error mapper --- .../src/utilities/errorMappers.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index da5f1a4259..5e8fc73e29 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -102,3 +102,19 @@ export const suspendedPurposeVersionErrorMapper = ( .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const createPurposeFromEServiceErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with("eserviceNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("eServiceModeNotAllowed", () => HTTP_STATUS_BAD_REQUEST) + .with("eserviceRiskAnalysisNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("missingFreeOfChargeReason", () => HTTP_STATUS_BAD_REQUEST) + .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) + .with("tenantKindNotFound", () => HTTP_STATUS_FORBIDDEN) + .with("agreementNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) + .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From c43019baac814a8aa8aec3a611d2e6210efad241 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 12:32:18 +0200 Subject: [PATCH 307/537] Implement endpoint --- .../src/services/purposeService.ts | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index de50adee8f..8b103fe4a3 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -3,6 +3,8 @@ import { DB, eventRepository, logger, + riskAnalysisFormToRiskAnalysisFormToValidate, + validateRiskAnalysis, } from "pagopa-interop-commons"; import { EService, @@ -27,12 +29,16 @@ import { ListResult, unsafeBrandId, generateId, + eserviceMode, + RiskAnalysisId, + RiskAnalysis, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { agreementNotFound, duplicatedPurposeName, eserviceNotFound, + eserviceRiskAnalysisNotFound, notValidVersionState, organizationIsNotTheConsumer, organizationIsNotTheProducer, @@ -41,6 +47,7 @@ import { purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, + riskAnalysisValidationFailed, tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; @@ -60,6 +67,7 @@ import { ApiReversePurposeUpdateContent, ApiGetPurposesFilters, ApiPurposeSeed, + ApiReversePurposeSeed, } from "../model/domain/models.js"; import { ReadModelService } from "./readModelService.js"; import { @@ -141,6 +149,21 @@ const retrieveTenant = async ( return tenant; }; +const retrieveRiskAnalysis = ( + riskAnalysisId: RiskAnalysisId, + eservice: EService +): RiskAnalysis => { + const riskAnalysis = eservice.riskAnalysis.find( + (ra: RiskAnalysis) => ra.id === riskAnalysisId + ); + + if (riskAnalysis === undefined) { + throw eserviceRiskAnalysisNotFound(eservice.id, riskAnalysisId); + } + + return riskAnalysis; +}; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( dbInstance: DB, @@ -593,6 +616,88 @@ export function purposeServiceBuilder( await repository.createEvent(event); return { purpose, isRiskAnalysisValid: validatedFormSeed !== undefined }; }, + async createPurposeFromEService( + organizationId: TenantId, + seed: ApiReversePurposeSeed, + correlationId: string + ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + const eserviceId: EServiceId = unsafeBrandId(seed.eServiceId); + const consumerId: TenantId = unsafeBrandId(seed.consumerId); + + assertOrganizationIsAConsumer(organizationId, consumerId); + const eservice = await retrieveEService(eserviceId, readModelService); + assertEserviceHasSpecificMode(eservice, eserviceMode.receive); + + const riskAnalysis = retrieveRiskAnalysis( + unsafeBrandId(seed.riskAnalysisId), + eservice + ); + + assertConsistentFreeOfCharge( + seed.isFreeOfCharge, + seed.freeOfChargeReason + ); + + const tenant = await retrieveTenant( + eservice.producerId, + readModelService + ); + + if (tenant.kind === undefined) { + throw tenantKindNotFound(tenant.id); + } + + const agreement = await readModelService.getActiveAgreement( + eserviceId, + consumerId + ); + + if (agreement === undefined) { + throw agreementNotFound(eserviceId, consumerId); + } + + const purposeWithSameName = await readModelService.getSpecificPurpose( + eserviceId, + consumerId, + seed.title + ); + + if (purposeWithSameName) { + throw duplicatedPurposeName(seed.title); + } + + const validationResult = validateRiskAnalysis( + riskAnalysisFormToRiskAnalysisFormToValidate( + riskAnalysis.riskAnalysisForm + ), + false, + tenant.kind + ); + + if (validationResult.type === "invalid") { + throw riskAnalysisValidationFailed(validationResult.issues); + } + + const purpose: Purpose = { + title: seed.title, + id: generateId(), + createdAt: new Date(), + eserviceId, + consumerId, + description: seed.description, + versions: [], + isFreeOfCharge: seed.isFreeOfCharge, + freeOfChargeReason: seed.freeOfChargeReason, + riskAnalysisForm: riskAnalysis.riskAnalysisForm, + }; + + const event = toCreateEventPurposeAdded(purpose, correlationId); + await repository.createEvent(event); + return { + purpose, + isRiskAnalysisValid: validationResult.type === "valid", + }; + }, }; } From 28fd8e69ec9afcf28a2a283316ab038dffd78822 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 12:32:27 +0200 Subject: [PATCH 308/537] Update router --- .../src/routers/PurposeRouter.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 3f9ee6a952..34969b3583 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -23,6 +23,7 @@ import { makeApiProblem } from "../model/domain/errors.js"; import { archivePurposeVersionErrorMapper, createPurposeErrorMapper, + createPurposeFromEServiceErrorMapper, deletePurposeErrorMapper, deletePurposeVersionErrorMapper, getPurposeErrorMapper, @@ -134,8 +135,26 @@ const purposeRouter = ( .post( "/reverse/purposes", authorizationMiddleware([ADMIN_ROLE]), - // TO DO - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.createPurposeFromEService( + req.ctx.authData.organizationId, + req.body, + req.ctx.correlationId + ); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + createPurposeFromEServiceErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/reverse/purposes/:id", From e75d0c0aff652b86339091713c1b56fa5e8b4036 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 14:17:34 +0200 Subject: [PATCH 309/537] Add logging --- packages/purpose-process/src/services/purposeService.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 8b103fe4a3..06bc8a4742 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -621,6 +621,9 @@ export function purposeServiceBuilder( seed: ApiReversePurposeSeed, correlationId: string ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + logger.info( + `Creating Purposes for EService ${seed.eServiceId}, Consumer ${seed.consumerId}` + ); const eserviceId: EServiceId = unsafeBrandId(seed.eServiceId); const consumerId: TenantId = unsafeBrandId(seed.consumerId); From 14938f30f61f8eab084186660a6aa155ed83768f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 14:17:40 +0200 Subject: [PATCH 310/537] Minor refactor --- packages/purpose-process/src/services/purposeService.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 06bc8a4742..dca6e664d8 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -646,9 +646,7 @@ export function purposeServiceBuilder( readModelService ); - if (tenant.kind === undefined) { - throw tenantKindNotFound(tenant.id); - } + assertTenantKindExists(tenant); const agreement = await readModelService.getActiveAgreement( eserviceId, From 4cd8e5dfdd7c3d529e0bfbb13f71bbee55cd9726 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 15:19:55 +0200 Subject: [PATCH 311/537] Fix sorting --- packages/purpose-process/src/services/readModelService.ts | 2 +- packages/purpose-process/test/testGetPurposes.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 85c9bc5c4f..8905d0b690 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -202,7 +202,7 @@ export function readModelServiceBuilder( { $project: { data: 1, - computedColumn: { $toLower: ["$data.name"] }, + computedColumn: { $toLower: ["$data.title"] }, }, }, { diff --git a/packages/purpose-process/test/testGetPurposes.ts b/packages/purpose-process/test/testGetPurposes.ts index bc10d9614a..2d5d4b2c36 100644 --- a/packages/purpose-process/test/testGetPurposes.ts +++ b/packages/purpose-process/test/testGetPurposes.ts @@ -365,6 +365,7 @@ export const testGetPurposes = (): ReturnType => it("should not include the riskAnalysisForm if the requester is not the producer nor the consumer", async () => { const mockPurpose8: Purpose = { ...getMockPurpose(), + title: "purpose 8", riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), eserviceId: mockEService4.id, }; @@ -400,6 +401,7 @@ export const testGetPurposes = (): ReturnType => const mockPurpose8: Purpose = { ...getMockPurpose(), + title: "purpose 8", riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), eserviceId: mockEService2ByTenant1.id, }; @@ -407,6 +409,7 @@ export const testGetPurposes = (): ReturnType => const mockPurpose9: Purpose = { ...getMockPurpose(), + title: "purpose 9", riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), eserviceId: mockEService4.id, }; From 85bbbf51d183a0b2eb0e09470b1c0b1f7c2df09a Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 23 Apr 2024 15:21:30 +0200 Subject: [PATCH 312/537] Implemented tests --- .../src/services/purposeService.ts | 10 +- .../src/services/readModelService.ts | 17 +- .../src/services/validators.ts | 1 - .../test/purposeService.integration.test.ts | 312 +++++++++++++++++- packages/purpose-process/test/utils.ts | 82 +++++ 5 files changed, 399 insertions(+), 23 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index de50adee8f..cb0fa8dab7 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -30,7 +30,6 @@ import { } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { - agreementNotFound, duplicatedPurposeName, eserviceNotFound, notValidVersionState, @@ -557,14 +556,7 @@ export function purposeServiceBuilder( tenant.kind ); - const agreement = await readModelService.getActiveAgreement( - eserviceId, - consumerId - ); - - if (agreement === undefined) { - throw agreementNotFound(eserviceId, consumerId); - } + await readModelService.checkActiveAgreement(eserviceId, consumerId); const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 26005299e8..d02339a592 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -24,6 +24,7 @@ import { import { Filter, WithId } from "mongodb"; import { z } from "zod"; import { ApiGetPurposesFilters } from "../model/domain/models.js"; +import { agreementNotFound } from "../model/domain/errors.js"; async function getPurpose( purposes: PurposeCollection, @@ -131,27 +132,19 @@ export function readModelServiceBuilder( }, } satisfies ReadModelFilter); }, - async getActiveAgreement( + async checkActiveAgreement( eserviceId: EServiceId, consumerId: TenantId - ): Promise { + ): Promise { const data = await agreements.findOne({ "data.eserviceId": eserviceId, "data.consumerId": consumerId, "data.state": agreementState.active, }); - - const result = Agreement.safeParse(data); + const result = Agreement.safeParse(data?.data); if (!result.success) { - logger.error( - `Unable to parse agreements items: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - - throw genericError("Unable to parse agreements items"); + throw agreementNotFound(eserviceId, consumerId); } - return result.data; }, async getPurposes( filters: ApiGetPurposesFilters, diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 64fa0eaabf..e28415bc02 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -93,7 +93,6 @@ export function validateAndTransformRiskAnalysis( if (!riskAnalysisForm) { return undefined; } - const validatedForm = validateRiskAnalysisSchemaOrThrow( riskAnalysisForm, tenantKind diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 5d43ec288b..210f4d168f 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ +import { fail } from "assert"; import { afterAll, afterEach, @@ -14,18 +15,24 @@ import { vi, } from "vitest"; import { + AgreementCollection, EServiceCollection, PurposeCollection, ReadModelRepository, TenantCollection, initDB, + unexpectedFieldError, + unexpectedFieldValueError, unexpectedRulesVersionError, + validateRiskAnalysis, } from "pagopa-interop-commons"; import { IDatabase } from "pg-promise"; import { + StoredEvent, TEST_MONGO_DB_PORT, TEST_POSTGRES_DB_PORT, decodeProtobufPayload, + eventStoreSchema, getMockPurpose, getMockPurposeVersion, getMockPurposeVersionDocument, @@ -64,7 +71,12 @@ import { toPurposeV2, toReadModelEService, unsafeBrandId, - PurposeVersionState, + descriptorState, + Descriptor, + PurposeAddedV2, + RiskAnalysisForm, + Agreement, + agreementState, } from "pagopa-interop-models"; import { config } from "../src/utilities/config.js"; import { @@ -76,6 +88,8 @@ import { readModelServiceBuilder, } from "../src/services/readModelService.js"; import { + agreementNotFound, + duplicatedPurposeName, eServiceModeNotAllowed, eserviceNotFound, missingFreeOfChargeReason, @@ -94,13 +108,19 @@ import { tenantNotFound, } from "../src/model/domain/errors.js"; import { + ApiPurposeSeed, ApiPurposeUpdateContent, ApiReversePurposeUpdateContent, } from "../src/model/domain/models.js"; import { + addOneAgreement, addOnePurpose, + addOneTenant, + buildRiskAnalysisFormSeed, buildRiskAnalysisSeed, createUpdatedPurpose, + getMockAgreement, + getMockDescriptor, getMockEService, } from "./utils.js"; @@ -108,6 +128,7 @@ describe("Integration tests", async () => { let purposes: PurposeCollection; let eservices: EServiceCollection; let tenants: TenantCollection; + let agreements: AgreementCollection; let readModelService: ReadModelService; let purposeService: PurposeService; let postgresDB: IDatabase; @@ -128,6 +149,7 @@ describe("Integration tests", async () => { purposes = readModelRepository.purposes; eservices = readModelRepository.eservices; tenants = readModelRepository.tenants; + agreements = readModelRepository.agreements; readModelService = readModelServiceBuilder(readModelRepository); postgresDB = initDB({ username: config.eventStoreDbUsername, @@ -1218,6 +1240,294 @@ describe("Integration tests", async () => { }); }); + describe("createPurpose", () => { + const tenant: Tenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + version: "", + }; + + const eService1: EService = { + ...getMockEService(), + producerId: tenant.id, + id: generateId(), + name: "A", + descriptors: [descriptor1], + }; + + const agreementEservice1 = getMockAgreement({ + eserviceId: eService1.id, + descriptorId: descriptor1.id, + producerId: eService1.producerId, + consumerId: tenant.id, + }); + + const mockValidRiskAnalysisForm = getMockValidRiskAnalysisForm( + tenantKind.PA + ); + + const purposeSeed: ApiPurposeSeed = { + eserviceId: eService1.id, + consumerId: agreementEservice1.consumerId, + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: true, + freeOfChargeReason: "reason", + riskAnalysisForm: buildRiskAnalysisFormSeed(mockValidRiskAnalysisForm), + }; + it("should write on event-store for the creation of a purpose", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(eService1), eservices); + + const { purpose } = await purposeService.createPurpose( + purposeSeed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ); + + const writtenEvent: StoredEvent | undefined = + await readLastEventByStreamId( + purpose.id, + eventStoreSchema.purpose, + postgresDB + ); + + if (!writtenEvent) { + fail("Update failed: purpose not found in event-store"); + } + + expect(writtenEvent).toMatchObject({ + stream_id: purpose.id, + version: "0", + type: "PurposeAdded", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeAddedV2, + payload: writtenEvent.data, + }); + + const expectedRiskAnalysisForm: RiskAnalysisForm = { + ...mockValidRiskAnalysisForm, + id: unsafeBrandId(purpose.riskAnalysisForm!.id), + singleAnswers: mockValidRiskAnalysisForm.singleAnswers.map( + (answer, i) => ({ + ...answer, + id: purpose.riskAnalysisForm!.singleAnswers[i].id, + }) + ), + multiAnswers: mockValidRiskAnalysisForm.multiAnswers.map( + (answer, i) => ({ + ...answer, + id: purpose.riskAnalysisForm!.multiAnswers[i].id, + }) + ), + }; + + const expectedPurpose: Purpose = { + title: purposeSeed.title, + id: unsafeBrandId(purpose.id), + createdAt: new Date(), + eserviceId: unsafeBrandId(purposeSeed.eserviceId), + consumerId: unsafeBrandId(purposeSeed.consumerId), + description: purposeSeed.description, + versions: [], + isFreeOfCharge: true, + freeOfChargeReason: purposeSeed.freeOfChargeReason, + riskAnalysisForm: expectedRiskAnalysisForm, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + vi.useRealTimers(); + }); + it("should throw missingFreeOfChargeReason if the freeOfChargeReason is empty", async () => { + const seed: ApiPurposeSeed = { + ...purposeSeed, + freeOfChargeReason: undefined, + }; + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(missingFreeOfChargeReason()); + }); + it("should throw tenantKindNotFound", async () => { + const tenantWithoutKind: Tenant = { + ...tenant, + kind: undefined, + }; + + const eService: EService = { + ...eService1, + producerId: tenantWithoutKind.id, + }; + + const agreementEservice = getMockAgreement({ + eserviceId: eService.id, + descriptorId: descriptor1.id, + producerId: eService.producerId, + consumerId: tenantWithoutKind.id, + }); + + const seed: ApiPurposeSeed = { + ...purposeSeed, + eserviceId: eService.id, + consumerId: agreementEservice.consumerId, + }; + + await addOneTenant(tenantWithoutKind, tenants); + await addOneAgreement(agreementEservice, agreements); + await writeInReadmodel(toReadModelEService(eService), eservices); + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(tenantKindNotFound(tenantWithoutKind.id)); + }); + it("should throw tenantNotFound", async () => { + expect( + purposeService.createPurpose( + purposeSeed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + }); + it("should throw agreementNotFound", async () => { + const tenantForDraftAgreement: Tenant = { + ...tenant, + id: generateId(), + }; + + const descriptor: Descriptor = { + ...descriptor1, + id: generateId(), + }; + + const eService: EService = { + ...eService1, + producerId: tenantForDraftAgreement.id, + id: generateId(), + descriptors: [descriptor], + }; + + const agreement: Agreement = { + ...agreementEservice1, + eserviceId: eService.id, + descriptorId: descriptor.id, + producerId: eService.producerId, + consumerId: tenantForDraftAgreement.id, + state: agreementState.draft, + }; + + const seed: ApiPurposeSeed = { + ...purposeSeed, + eserviceId: eService.id, + consumerId: agreement.consumerId, + }; + + await addOneTenant(tenantForDraftAgreement, tenants); + await addOneAgreement(agreement, agreements); + await writeInReadmodel(toReadModelEService(eService), eservices); + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(seed.consumerId), + generateId() + ) + ).rejects.toThrowError( + agreementNotFound(eService.id, tenantForDraftAgreement.id) + ); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const seed: ApiPurposeSeed = { + ...purposeSeed, + consumerId: generateId(), + }; + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError( + organizationIsNotTheConsumer(unsafeBrandId(purposeSeed.consumerId)) + ); + }); + it("should throw riskAnalysisValidationFailed if the purpose has an inactive risk analysis ", async () => { + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(eService1), eservices); + + const mockInvalidRiskAnalysisForm: RiskAnalysisForm = { + ...mockValidRiskAnalysisForm, + version: "251", + }; + + const seed: ApiPurposeSeed = { + ...purposeSeed, + riskAnalysisForm: buildRiskAnalysisFormSeed( + mockInvalidRiskAnalysisForm + ), + }; + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError( + riskAnalysisValidationFailed([ + unexpectedRulesVersionError(mockInvalidRiskAnalysisForm.version), + ]) + ); + }); + it("should throw duplicatedPurposeName if exist alreay a purpose with same name", async () => { + const existingPurpose: Purpose = { + ...mockPurpose, + eserviceId: unsafeBrandId(purposeSeed.eserviceId), + consumerId: unsafeBrandId(purposeSeed.consumerId), + title: purposeSeed.title, + }; + + await addOnePurpose(existingPurpose, postgresDB, purposes); + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(eService1), eservices); + + expect( + purposeService.createPurpose( + purposeSeed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(duplicatedPurposeName(purposeSeed.title)); + }); + }); + describe("deletePurpose", () => { it("should write on event-store for the deletion of a purpose (no versions)", async () => { const mockEService = getMockEService(); diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 87b379bad8..a1c4b362f9 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { + AgreementCollection, PurposeCollection, + TenantCollection, riskAnalysisFormToRiskAnalysisFormToValidate, } from "pagopa-interop-commons"; import { @@ -8,11 +10,20 @@ import { writeInReadmodel, } from "pagopa-interop-commons-test"; import { + Agreement, + Descriptor, + DescriptorId, DraftPurposeUpdatedV2, EService, + EServiceId, Purpose, PurposeEvent, RiskAnalysis, + RiskAnalysisForm, + Tenant, + TenantId, + agreementState, + descriptorState, generateId, purposeEventToBinaryData, technology, @@ -35,6 +46,20 @@ export const addOnePurpose = async ( await writeInReadmodel(purpose, purposes); }; +export const addOneTenant = async ( + tenant: Tenant, + tenants: TenantCollection +): Promise => { + await writeInReadmodel(tenant, tenants); +}; + +export const addOneAgreement = async ( + agreement: Agreement, + agreements: AgreementCollection +): Promise => { + await writeInReadmodel(agreement, agreements); +}; + export const writePurposeInEventstore = async ( purpose: Purpose, postgresDB: IDatabase @@ -56,6 +81,58 @@ export const writePurposeInEventstore = async ( await writeInEventstore(eventToWrite, "purpose", postgresDB); }; +export const getMockAgreement = ({ + eserviceId, + descriptorId, + producerId, + consumerId, +}: { + eserviceId: EServiceId; + descriptorId: DescriptorId; + producerId: TenantId; + consumerId: TenantId; +}): Agreement => ({ + id: generateId(), + createdAt: new Date(), + eserviceId, + descriptorId, + producerId, + consumerId, + state: agreementState.active, + verifiedAttributes: [], + certifiedAttributes: [], + declaredAttributes: [], + consumerDocuments: [], + stamps: { + submission: undefined, + activation: undefined, + rejection: undefined, + suspensionByProducer: undefined, + suspensionByConsumer: undefined, + upgrade: undefined, + archiving: undefined, + }, +}); + +export const getMockDescriptor = (): Descriptor => ({ + id: generateId(), + version: "1", + docs: [], + state: descriptorState.draft, + audience: [], + voucherLifespan: 60, + dailyCallsPerConsumer: 10, + dailyCallsTotal: 1000, + createdAt: new Date(), + serverUrls: ["pagopa.it"], + agreementApprovalPolicy: "Automatic", + attributes: { + certified: [], + verified: [], + declared: [], + }, +}); + export const getMockEService = (): EService => ({ id: generateId(), name: "eService name", @@ -74,6 +151,11 @@ export const buildRiskAnalysisSeed = ( ): ApiRiskAnalysisFormSeed => riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysis.riskAnalysisForm); +export const buildRiskAnalysisFormSeed = ( + riskAnalysisForm: RiskAnalysisForm +): ApiRiskAnalysisFormSeed => + riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm); + export const createUpdatedPurpose = ( mockPurpose: Purpose, purposeUpdateContent: From f84244d407061719488de1f4c857ab5df1bb362e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 15:38:33 +0200 Subject: [PATCH 313/537] Fix agreement query --- .../src/services/readModelService.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 2ab31c3e22..301eb66451 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -134,24 +134,26 @@ export function readModelServiceBuilder( async getActiveAgreement( eserviceId: EServiceId, consumerId: TenantId - ): Promise { + ): Promise { const data = await agreements.findOne({ "data.eserviceId": eserviceId, "data.consumerId": consumerId, "data.state": agreementState.active, }); - - const result = Agreement.safeParse(data); - if (!result.success) { - logger.error( - `Unable to parse agreements items: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - - throw genericError("Unable to parse agreements items"); + if (!data) { + return undefined; + } else { + const result = Agreement.safeParse(data.data); + if (!result.success) { + logger.error( + `Unable to parse agreement item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse agreement item"); + } + return result.data; } - return result.data; }, async getPurposes( filters: ApiGetPurposesFilters, From bdd8091e10d629a988b6346d3be7b5b842afb4d5 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 23 Apr 2024 15:55:49 +0200 Subject: [PATCH 314/537] WIP --- .../src/services/purposeService.ts | 10 +++++++++- .../src/services/readModelService.ts | 20 ++++++++++++++----- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d16d5826d6..acc7db796f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -30,6 +30,7 @@ import { } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { + agreementNotFound, duplicatedPurposeName, eserviceNotFound, notValidVersionState, @@ -584,7 +585,14 @@ export function purposeServiceBuilder( tenant.kind ); - await readModelService.checkActiveAgreement(eserviceId, consumerId); + const agreement = await readModelService.getActiveAgreement( + eserviceId, + consumerId + ); + + if (agreement === undefined) { + agreementNotFound(eserviceId, consumerId); + } const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index ecadfe64d9..1b1d58b7d4 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -132,18 +132,28 @@ export function readModelServiceBuilder( }, } satisfies ReadModelFilter); }, - async checkActiveAgreement( + async getActiveAgreement( eserviceId: EServiceId, consumerId: TenantId - ): Promise { + ): Promise { const data = await agreements.findOne({ "data.eserviceId": eserviceId, "data.consumerId": consumerId, "data.state": agreementState.active, }); - const result = Agreement.safeParse(data?.data); - if (!result.success) { - throw agreementNotFound(eserviceId, consumerId); + if (!data) { + return undefined; + } else { + const result = Agreement.safeParse(data.data); + if (!result.success) { + logger.error( + `Unable to parse agreement item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse agreement item"); + } + return result.data; } }, async getPurposes( From cc79851af7739d9a31d4d588acd5c390a28593dc Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 23 Apr 2024 15:59:39 +0200 Subject: [PATCH 315/537] fix import --- packages/purpose-process/src/services/readModelService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 1b1d58b7d4..5a29f3bd1c 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -24,7 +24,6 @@ import { import { Filter, WithId } from "mongodb"; import { z } from "zod"; import { ApiGetPurposesFilters } from "../model/domain/models.js"; -import { agreementNotFound } from "../model/domain/errors.js"; async function getPurpose( purposes: PurposeCollection, From 7d0480f8cf85964c9c84b60cb544524e269f5739 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 23 Apr 2024 16:14:13 +0200 Subject: [PATCH 316/537] Split test file --- .../src/model/domain/errors.ts | 2 +- .../src/services/purposeService.ts | 5 +- .../test/purposeService.integration.test.ts | 2 + .../purpose-process/test/testCreatePurpose.ts | 337 ++++++++++++++++++ 4 files changed, 341 insertions(+), 5 deletions(-) create mode 100644 packages/purpose-process/test/testCreatePurpose.ts diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 1e4beae644..9b943f8591 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -195,7 +195,7 @@ export function agreementNotFound( export function duplicatedPurposeName(title: string): ApiError { return new ApiError({ - detail: `Purpose with name: ${title} already in use`, + detail: `Purpose with name: ${title} already exists`, code: "duplicatedPurposeName", title: "Duplicated Purpose Name", }); diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index acc7db796f..284a5fa261 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -41,7 +41,6 @@ import { purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, - tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; import { @@ -576,9 +575,7 @@ export function purposeServiceBuilder( const tenant = await retrieveTenant(organizationId, readModelService); - if (!tenant.kind) { - throw tenantKindNotFound(tenant.id); - } + assertTenantKindExists(tenant); const validatedFormSeed = validateAndTransformRiskAnalysis( purposeSeed.riskAnalysisForm, diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index c7fe30ce52..ebbfe3073d 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -38,6 +38,7 @@ import { testDeletePurpose } from "./testDeletePurpose.js"; import { testArchivePurposeVersion } from "./testArchivePurposeVersion.js"; import { testSuspendPurposeVersion } from "./testSuspendPurposeVersion.js"; import { testGetPurposes } from "./testGetPurposes.js"; +import { testCreatePurpose } from "./testCreatePurpose.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -101,5 +102,6 @@ describe("Integration tests", async () => { testArchivePurposeVersion(); testSuspendPurposeVersion(); testGetPurposes(); + testCreatePurpose(); }); }); diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts new file mode 100644 index 0000000000..cc130bb03e --- /dev/null +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -0,0 +1,337 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + Agreement, + Descriptor, + EService, + Purpose, + PurposeAddedV2, + RiskAnalysisForm, + Tenant, + agreementState, + descriptorState, + generateId, + tenantKind, + toPurposeV2, + toReadModelEService, + unsafeBrandId, +} from "pagopa-interop-models"; +import { describe, expect, it, vi } from "vitest"; +import { + writeInReadmodel, + getMockValidRiskAnalysisForm, + decodeProtobufPayload, + getMockAgreement, + getMockTenant, + readLastEventByStreamId, + getMockPurpose, +} from "pagopa-interop-commons-test/index.js"; +import { unexpectedRulesVersionError } from "pagopa-interop-commons"; +import { + missingFreeOfChargeReason, + tenantKindNotFound, + tenantNotFound, + agreementNotFound, + organizationIsNotTheConsumer, + riskAnalysisValidationFailed, + duplicatedPurposeName, +} from "../src/model/domain/errors.js"; +import { ApiPurposeSeed } from "../src/model/domain/models.js"; +import { + postgresDB, + purposes, + eservices, + purposeService, + agreements, + tenants, +} from "./purposeService.integration.test.js"; +import { + addOneAgreement, + addOnePurpose, + addOneTenant, + buildRiskAnalysisFormSeed, + getMockDescriptor, + getMockEService, +} from "./utils.js"; + +export const testCreatePurpose = (): ReturnType => + describe("createPurpose", () => { + const tenant: Tenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + version: "", + }; + + const eService1: EService = { + ...getMockEService(), + producerId: tenant.id, + id: generateId(), + name: "A", + descriptors: [descriptor1], + }; + + const agreementEservice1 = getMockAgreement(eService1.id, tenant.id); + + const mockValidRiskAnalysisForm = getMockValidRiskAnalysisForm( + tenantKind.PA + ); + + const purposeSeed: ApiPurposeSeed = { + eserviceId: eService1.id, + consumerId: agreementEservice1.consumerId, + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: true, + freeOfChargeReason: "reason", + riskAnalysisForm: buildRiskAnalysisFormSeed(mockValidRiskAnalysisForm), + }; + it("should write on event-store for the creation of a purpose", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(eService1), eservices); + + const { purpose } = await purposeService.createPurpose( + purposeSeed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ); + + const writtenEvent = await readLastEventByStreamId( + purpose.id, + "purpose", + postgresDB + ); + + if (!writtenEvent) { + fail("Update failed: purpose not found in event-store"); + } + + expect(writtenEvent).toMatchObject({ + stream_id: purpose.id, + version: "0", + type: "PurposeAdded", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeAddedV2, + payload: writtenEvent.data, + }); + + const expectedRiskAnalysisForm: RiskAnalysisForm = { + ...mockValidRiskAnalysisForm, + id: unsafeBrandId(purpose.riskAnalysisForm!.id), + singleAnswers: mockValidRiskAnalysisForm.singleAnswers.map( + (answer, i) => ({ + ...answer, + id: purpose.riskAnalysisForm!.singleAnswers[i].id, + }) + ), + multiAnswers: mockValidRiskAnalysisForm.multiAnswers.map( + (answer, i) => ({ + ...answer, + id: purpose.riskAnalysisForm!.multiAnswers[i].id, + }) + ), + }; + + const expectedPurpose: Purpose = { + title: purposeSeed.title, + id: unsafeBrandId(purpose.id), + createdAt: new Date(), + eserviceId: unsafeBrandId(purposeSeed.eserviceId), + consumerId: unsafeBrandId(purposeSeed.consumerId), + description: purposeSeed.description, + versions: [], + isFreeOfCharge: true, + freeOfChargeReason: purposeSeed.freeOfChargeReason, + riskAnalysisForm: expectedRiskAnalysisForm, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + vi.useRealTimers(); + }); + it("should throw missingFreeOfChargeReason if the freeOfChargeReason is empty", async () => { + const seed: ApiPurposeSeed = { + ...purposeSeed, + freeOfChargeReason: undefined, + }; + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(missingFreeOfChargeReason()); + }); + it("should throw tenantKindNotFound", async () => { + const tenantWithoutKind: Tenant = { + ...tenant, + kind: undefined, + }; + + const eService: EService = { + ...eService1, + producerId: tenantWithoutKind.id, + }; + + const agreementEservice = getMockAgreement( + eService.id, + tenantWithoutKind.id + ); + + const seed: ApiPurposeSeed = { + ...purposeSeed, + eserviceId: eService.id, + consumerId: agreementEservice.consumerId, + }; + + await addOneTenant(tenantWithoutKind, tenants); + await addOneAgreement(agreementEservice, agreements); + await writeInReadmodel(toReadModelEService(eService), eservices); + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(tenantKindNotFound(tenantWithoutKind.id)); + }); + it("should throw tenantNotFound", async () => { + expect( + purposeService.createPurpose( + purposeSeed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(tenantNotFound(tenant.id)); + }); + it("should throw agreementNotFound", async () => { + const tenantForDraftAgreement: Tenant = { + ...tenant, + id: generateId(), + }; + + const descriptor: Descriptor = { + ...descriptor1, + id: generateId(), + }; + + const eService: EService = { + ...eService1, + producerId: tenantForDraftAgreement.id, + id: generateId(), + descriptors: [descriptor], + }; + + const agreement: Agreement = { + ...agreementEservice1, + eserviceId: eService.id, + descriptorId: descriptor.id, + producerId: eService.producerId, + consumerId: tenantForDraftAgreement.id, + state: agreementState.draft, + }; + + const seed: ApiPurposeSeed = { + ...purposeSeed, + eserviceId: eService.id, + consumerId: agreement.consumerId, + }; + + await addOneTenant(tenantForDraftAgreement, tenants); + await addOneAgreement(agreement, agreements); + await writeInReadmodel(toReadModelEService(eService), eservices); + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(seed.consumerId), + generateId() + ) + ).rejects.toThrowError( + agreementNotFound(eService.id, tenantForDraftAgreement.id) + ); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(getMockEService()), eservices); + + const seed: ApiPurposeSeed = { + ...purposeSeed, + consumerId: generateId(), + }; + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError( + organizationIsNotTheConsumer(unsafeBrandId(purposeSeed.consumerId)) + ); + }); + it("should throw riskAnalysisValidationFailed if the purpose has an inactive risk analysis ", async () => { + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(eService1), eservices); + + const mockInvalidRiskAnalysisForm: RiskAnalysisForm = { + ...mockValidRiskAnalysisForm, + version: "251", + }; + + const seed: ApiPurposeSeed = { + ...purposeSeed, + riskAnalysisForm: buildRiskAnalysisFormSeed( + mockInvalidRiskAnalysisForm + ), + }; + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError( + riskAnalysisValidationFailed([ + unexpectedRulesVersionError(mockInvalidRiskAnalysisForm.version), + ]) + ); + }); + it("should throw duplicatedPurposeName if exist alreay a purpose with same name", async () => { + const existingPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: unsafeBrandId(purposeSeed.eserviceId), + consumerId: unsafeBrandId(purposeSeed.consumerId), + title: purposeSeed.title, + }; + + await addOnePurpose(existingPurpose, postgresDB, purposes); + await addOneTenant(tenant, tenants); + await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(toReadModelEService(eService1), eservices); + + expect( + purposeService.createPurpose( + purposeSeed, + unsafeBrandId(purposeSeed.consumerId), + generateId() + ) + ).rejects.toThrowError(duplicatedPurposeName(purposeSeed.title)); + }); + }); From f7bb23a0568cdf4383c0aeb6f322339c2566618f Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 23 Apr 2024 16:51:37 +0200 Subject: [PATCH 317/537] fix --- packages/commons-test/src/testUtils.ts | 19 +++++ .../src/model/domain/errors.ts | 20 ++--- .../src/services/purposeService.ts | 2 +- .../src/utilities/errorMappers.ts | 22 +++--- .../test/purposeService.integration.test.ts | 1 + .../purpose-process/test/testCreatePurpose.ts | 50 ++++++------ packages/purpose-process/test/utils.ts | 76 ------------------- 7 files changed, 65 insertions(+), 125 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 10a5ae6491..2897a353a8 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -210,3 +210,22 @@ export const getMockPurposeVersionDocument = (): PurposeVersionDocument => ({ contentType: "json", createdAt: new Date(), }); + +export const getMockDescriptor = (): Descriptor => ({ + id: generateId(), + version: "1", + docs: [], + state: descriptorState.draft, + audience: [], + voucherLifespan: 60, + dailyCallsPerConsumer: 10, + dailyCallsTotal: 1000, + createdAt: new Date(), + serverUrls: ["pagopa.it"], + agreementApprovalPolicy: "Automatic", + attributes: { + certified: [], + verified: [], + declared: [], + }, +}); diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 9b943f8591..346762145f 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -182,6 +182,16 @@ export function notValidVersionState( }); } +export function purposeCannotBeDeleted( + purposeId: PurposeId +): ApiError { + return new ApiError({ + detail: `Purpose ${purposeId} cannot be deleted`, + code: "purposeCannotBeDeleted", + title: "Purpose canont be deleted", + }); +} + export function agreementNotFound( eserviceId: EServiceId, consumerId: TenantId @@ -200,13 +210,3 @@ export function duplicatedPurposeName(title: string): ApiError { title: "Duplicated Purpose Name", }); } - -export function purposeCannotBeDeleted( - purposeId: PurposeId -): ApiError { - return new ApiError({ - detail: `Purpose ${purposeId} cannot be deleted`, - code: "purposeCannotBeDeleted", - title: "Purpose canont be deleted", - }); -} diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 284a5fa261..da76cd3d33 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -588,7 +588,7 @@ export function purposeServiceBuilder( ); if (agreement === undefined) { - agreementNotFound(eserviceId, consumerId); + throw agreementNotFound(eserviceId, consumerId); } const purposeWithSameName = await readModelService.getSpecificPurpose( diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index dd9da60d56..e402e9ede5 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -21,17 +21,6 @@ export const getPurposeErrorMapper = (error: ApiError): number => .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); -export const createPurposeErrorMapper = (error: ApiError): number => - match(error.code) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) - .with("missingFreeOfChargeReason", () => HTTP_STATUS_NOT_FOUND) - .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_FORBIDDEN) - .with("agreementNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("duplicatedPurposeName", () => HTTP_STATUS_FORBIDDEN) - .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); - export const getRiskAnalysisDocumentErrorMapper = ( error: ApiError ): number => @@ -101,3 +90,14 @@ export const suspendedPurposeVersionErrorMapper = ( .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const createPurposeErrorMapper = (error: ApiError): number => + match(error.code) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) + .with("missingFreeOfChargeReason", () => HTTP_STATUS_NOT_FOUND) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("riskAnalysisValidationFailed", () => HTTP_STATUS_FORBIDDEN) + .with("agreementNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("duplicatedPurposeName", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ebbfe3073d..d1821a70d8 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -84,6 +84,7 @@ describe("Integration tests", async () => { await purposes.deleteMany({}); await tenants.deleteMany({}); await eservices.deleteMany({}); + await agreements.deleteMany({}); await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); }); diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts index cc130bb03e..12c3f05580 100644 --- a/packages/purpose-process/test/testCreatePurpose.ts +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -26,7 +26,8 @@ import { getMockTenant, readLastEventByStreamId, getMockPurpose, -} from "pagopa-interop-commons-test/index.js"; + getMockDescriptor, +} from "pagopa-interop-commons-test"; import { unexpectedRulesVersionError } from "pagopa-interop-commons"; import { missingFreeOfChargeReason, @@ -47,11 +48,8 @@ import { tenants, } from "./purposeService.integration.test.js"; import { - addOneAgreement, addOnePurpose, - addOneTenant, buildRiskAnalysisFormSeed, - getMockDescriptor, getMockEService, } from "./utils.js"; @@ -76,7 +74,11 @@ export const testCreatePurpose = (): ReturnType => descriptors: [descriptor1], }; - const agreementEservice1 = getMockAgreement(eService1.id, tenant.id); + const agreementEservice1 = getMockAgreement( + eService1.id, + tenant.id, + agreementState.active + ); const mockValidRiskAnalysisForm = getMockValidRiskAnalysisForm( tenantKind.PA @@ -95,8 +97,8 @@ export const testCreatePurpose = (): ReturnType => it("should write on event-store for the creation of a purpose", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); - await addOneTenant(tenant, tenants); - await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(tenant, tenants); + await writeInReadmodel(agreementEservice1, agreements); await writeInReadmodel(toReadModelEService(eService1), eservices); const { purpose } = await purposeService.createPurpose( @@ -196,8 +198,8 @@ export const testCreatePurpose = (): ReturnType => consumerId: agreementEservice.consumerId, }; - await addOneTenant(tenantWithoutKind, tenants); - await addOneAgreement(agreementEservice, agreements); + await writeInReadmodel(tenantWithoutKind, tenants); + await writeInReadmodel(agreementEservice, agreements); await writeInReadmodel(toReadModelEService(eService), eservices); expect( @@ -218,11 +220,6 @@ export const testCreatePurpose = (): ReturnType => ).rejects.toThrowError(tenantNotFound(tenant.id)); }); it("should throw agreementNotFound", async () => { - const tenantForDraftAgreement: Tenant = { - ...tenant, - id: generateId(), - }; - const descriptor: Descriptor = { ...descriptor1, id: generateId(), @@ -230,17 +227,18 @@ export const testCreatePurpose = (): ReturnType => const eService: EService = { ...eService1, - producerId: tenantForDraftAgreement.id, + producerId: tenant.id, id: generateId(), descriptors: [descriptor], }; const agreement: Agreement = { ...agreementEservice1, + id: generateId(), eserviceId: eService.id, descriptorId: descriptor.id, producerId: eService.producerId, - consumerId: tenantForDraftAgreement.id, + consumerId: tenant.id, state: agreementState.draft, }; @@ -250,8 +248,8 @@ export const testCreatePurpose = (): ReturnType => consumerId: agreement.consumerId, }; - await addOneTenant(tenantForDraftAgreement, tenants); - await addOneAgreement(agreement, agreements); + await writeInReadmodel(tenant, tenants); + await writeInReadmodel(agreement, agreements); await writeInReadmodel(toReadModelEService(eService), eservices); expect( @@ -260,13 +258,11 @@ export const testCreatePurpose = (): ReturnType => unsafeBrandId(seed.consumerId), generateId() ) - ).rejects.toThrowError( - agreementNotFound(eService.id, tenantForDraftAgreement.id) - ); + ).rejects.toThrowError(agreementNotFound(eService.id, tenant.id)); }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { - await addOneTenant(tenant, tenants); - await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(tenant, tenants); + await writeInReadmodel(agreementEservice1, agreements); await writeInReadmodel(toReadModelEService(getMockEService()), eservices); const seed: ApiPurposeSeed = { @@ -285,8 +281,8 @@ export const testCreatePurpose = (): ReturnType => ); }); it("should throw riskAnalysisValidationFailed if the purpose has an inactive risk analysis ", async () => { - await addOneTenant(tenant, tenants); - await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(tenant, tenants); + await writeInReadmodel(agreementEservice1, agreements); await writeInReadmodel(toReadModelEService(eService1), eservices); const mockInvalidRiskAnalysisForm: RiskAnalysisForm = { @@ -322,8 +318,8 @@ export const testCreatePurpose = (): ReturnType => }; await addOnePurpose(existingPurpose, postgresDB, purposes); - await addOneTenant(tenant, tenants); - await addOneAgreement(agreementEservice1, agreements); + await writeInReadmodel(tenant, tenants); + await writeInReadmodel(agreementEservice1, agreements); await writeInReadmodel(toReadModelEService(eService1), eservices); expect( diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 1f42a9c736..63766d9f8b 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -1,8 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { - AgreementCollection, PurposeCollection, - TenantCollection, riskAnalysisFormToRiskAnalysisFormToValidate, } from "pagopa-interop-commons"; import { @@ -11,20 +9,12 @@ import { writeInReadmodel, } from "pagopa-interop-commons-test"; import { - Agreement, - Descriptor, - DescriptorId, DraftPurposeUpdatedV2, EService, - EServiceId, Purpose, PurposeEvent, RiskAnalysis, RiskAnalysisForm, - Tenant, - TenantId, - agreementState, - descriptorState, generateId, technology, toPurposeV2, @@ -46,20 +36,6 @@ export const addOnePurpose = async ( await writeInReadmodel(purpose, purposes); }; -export const addOneTenant = async ( - tenant: Tenant, - tenants: TenantCollection -): Promise => { - await writeInReadmodel(tenant, tenants); -}; - -export const addOneAgreement = async ( - agreement: Agreement, - agreements: AgreementCollection -): Promise => { - await writeInReadmodel(agreement, agreements); -}; - export const writePurposeInEventstore = async ( purpose: Purpose, postgresDB: IDatabase @@ -79,58 +55,6 @@ export const writePurposeInEventstore = async ( await writeInEventstore(eventToWrite, "purpose", postgresDB); }; -export const getMockAgreement = ({ - eserviceId, - descriptorId, - producerId, - consumerId, -}: { - eserviceId: EServiceId; - descriptorId: DescriptorId; - producerId: TenantId; - consumerId: TenantId; -}): Agreement => ({ - id: generateId(), - createdAt: new Date(), - eserviceId, - descriptorId, - producerId, - consumerId, - state: agreementState.active, - verifiedAttributes: [], - certifiedAttributes: [], - declaredAttributes: [], - consumerDocuments: [], - stamps: { - submission: undefined, - activation: undefined, - rejection: undefined, - suspensionByProducer: undefined, - suspensionByConsumer: undefined, - upgrade: undefined, - archiving: undefined, - }, -}); - -export const getMockDescriptor = (): Descriptor => ({ - id: generateId(), - version: "1", - docs: [], - state: descriptorState.draft, - audience: [], - voucherLifespan: 60, - dailyCallsPerConsumer: 10, - dailyCallsTotal: 1000, - createdAt: new Date(), - serverUrls: ["pagopa.it"], - agreementApprovalPolicy: "Automatic", - attributes: { - certified: [], - verified: [], - declared: [], - }, -}); - export const getMockEService = (): EService => ({ id: generateId(), name: "eService name", From ab34e9c370cec8b3d79347a96b3e6ede6c107b37 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 23 Apr 2024 16:59:46 +0200 Subject: [PATCH 318/537] minor changes --- .../src/model/domain/toEvent.ts | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index f909edcee1..daf92445d0 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -69,23 +69,6 @@ export const toCreateEventDraftPurposeUpdated = ({ correlationId, }); -export function toCreateEventPurposeAdded( - purpose: Purpose, - correlationId: string -): CreateEvent { - return { - streamId: purpose.id, - version: 0, - event: { - type: "PurposeAdded", - event_version: 2, - data: { - purpose: toPurposeV2(purpose), - }, - }, - correlationId, - }; -} export const toCreateEventDraftPurposeDeleted = ({ purpose, version, @@ -186,3 +169,21 @@ export const toCreateEventPurposeSuspendedByProducer = ({ }, correlationId, }); + +export function toCreateEventPurposeAdded( + purpose: Purpose, + correlationId: string +): CreateEvent { + return { + streamId: purpose.id, + version: 0, + event: { + type: "PurposeAdded", + event_version: 2, + data: { + purpose: toPurposeV2(purpose), + }, + }, + correlationId, + }; +} From 31c54949fe0620670af61772fe1aa127d112efc1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:00:29 +0200 Subject: [PATCH 319/537] Fix typo --- packages/purpose-process/src/routers/PurposeRouter.ts | 4 ++-- packages/purpose-process/src/utilities/errorMappers.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 5e03b43a5c..701b5b55d1 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -26,7 +26,7 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, - suspendedPurposeVersionErrorMapper, + suspendPurposeVersionErrorMapper, updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; @@ -267,7 +267,7 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - suspendedPurposeVersionErrorMapper + suspendPurposeVersionErrorMapper ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 6338b21587..2ba2aff55a 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -81,7 +81,7 @@ export const archivePurposeVersionErrorMapper = ( .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); -export const suspendedPurposeVersionErrorMapper = ( +export const suspendPurposeVersionErrorMapper = ( error: ApiError ): number => match(error.code) From e9b5a3f8ef1d4fdf0495f291090354d65eca9599 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:11:44 +0200 Subject: [PATCH 320/537] Renaming --- packages/purpose-process/src/services/purposeService.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 9b6514df2c..b563d69879 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -48,7 +48,6 @@ import { purposeVersionDocumentNotFound, purposeVersionNotFound, riskAnalysisValidationFailed, - tenantKindNotFound, tenantNotFound, } from "../model/domain/errors.js"; import { @@ -667,12 +666,12 @@ export function purposeServiceBuilder( seed.freeOfChargeReason ); - const tenant = await retrieveTenant( + const producer = await retrieveTenant( eservice.producerId, readModelService ); - assertTenantKindExists(tenant); + assertTenantKindExists(producer); const agreement = await readModelService.getActiveAgreement( eserviceId, @@ -698,7 +697,7 @@ export function purposeServiceBuilder( riskAnalysis.riskAnalysisForm ), false, - tenant.kind + producer.kind ); if (validationResult.type === "invalid") { From 57c881999010d009782ea9c45ff67f3222f80aa9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:11:52 +0200 Subject: [PATCH 321/537] Add tests --- packages/commons-test/src/testUtils.ts | 30 ++ .../test/purposeService.integration.test.ts | 4 + .../test/testCreateReversePurpose.ts | 401 ++++++++++++++++++ 3 files changed, 435 insertions(+) create mode 100644 packages/purpose-process/test/testCreateReversePurpose.ts diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 10a5ae6491..5cd17e5a4a 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -25,6 +25,7 @@ import { PurposeVersionDocument, PurposeVersionState, purposeVersionState, + Document, } from "pagopa-interop-models"; import { v4 as uuidv4 } from "uuid"; import { AuthData } from "pagopa-interop-commons"; @@ -63,6 +64,35 @@ export const getMockDescriptorPublished = ( }, }); +export const getMockDescriptor = (): Descriptor => ({ + id: generateId(), + version: "1", + docs: [], + state: descriptorState.draft, + audience: [], + voucherLifespan: 60, + dailyCallsPerConsumer: 10, + dailyCallsTotal: 1000, + createdAt: new Date(), + serverUrls: ["pagopa.it"], + agreementApprovalPolicy: "Automatic", + attributes: { + certified: [], + verified: [], + declared: [], + }, +}); + +export const getMockDocument = (): Document => ({ + name: "fileName", + path: "filePath", + id: generateId(), + prettyName: "prettyName", + contentType: "json", + checksum: "checksum", + uploadDate: new Date(), +}); + export const getMockEServiceAttribute = ( attributeId: AttributeId = generateId() ): EServiceAttribute => ({ diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ebbfe3073d..061431fb99 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -39,6 +39,7 @@ import { testArchivePurposeVersion } from "./testArchivePurposeVersion.js"; import { testSuspendPurposeVersion } from "./testSuspendPurposeVersion.js"; import { testGetPurposes } from "./testGetPurposes.js"; import { testCreatePurpose } from "./testCreatePurpose.js"; +import { testCreateReversePurpose } from "./testCreateReversePurpose.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -67,6 +68,7 @@ describe("Integration tests", async () => { eservices = readModelRepository.eservices; tenants = readModelRepository.tenants; agreements = readModelRepository.agreements; + readModelService = readModelServiceBuilder(readModelRepository); postgresDB = initDB({ username: config.eventStoreDbUsername, @@ -84,6 +86,7 @@ describe("Integration tests", async () => { await purposes.deleteMany({}); await tenants.deleteMany({}); await eservices.deleteMany({}); + await agreements.deleteMany({}); await postgresDB.none("TRUNCATE TABLE purpose.events RESTART IDENTITY"); }); @@ -103,5 +106,6 @@ describe("Integration tests", async () => { testSuspendPurposeVersion(); testGetPurposes(); testCreatePurpose(); + testCreateReversePurpose(); }); }); diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts new file mode 100644 index 0000000000..12859a6450 --- /dev/null +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -0,0 +1,401 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { describe, expect, it, vi } from "vitest"; +import { + decodeProtobufPayload, + getMockAgreement, + getMockDescriptor, + getMockDocument, + getMockTenant, + getMockValidRiskAnalysis, + readLastEventByStreamId, + writeInReadmodel, +} from "pagopa-interop-commons-test"; +import { + Agreement, + Descriptor, + EService, + Purpose, + PurposeAddedV2, + RiskAnalysisId, + Tenant, + agreementState, + descriptorState, + eserviceMode, + generateId, + tenantKind, + toPurposeV2, + toReadModelEService, + unsafeBrandId, +} from "pagopa-interop-models"; +import { ApiReversePurposeSeed } from "../src/model/domain/models.js"; +import { + eServiceModeNotAllowed, + eserviceRiskAnalysisNotFound, + missingFreeOfChargeReason, + organizationIsNotTheConsumer, + tenantKindNotFound, +} from "../src/model/domain/errors.js"; +import { getMockEService } from "./utils.js"; +import { + agreements, + eservices, + postgresDB, + purposeService, + tenants, +} from "./purposeService.integration.test.js"; + +export const testCreateReversePurpose = (): ReturnType => + describe("createReveresePurpose", () => { + it("should write in event-store for the creation of a reverse purpose", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + const { purpose } = await purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ); + + const writtenEvent = await readLastEventByStreamId( + purpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: purpose.id, + version: "0", + type: "PurposeAdded", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeAddedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + versions: [], + id: purpose.id, + createdAt: new Date(), + eserviceId: unsafeBrandId(reversePurposeSeed.eServiceId), + consumerId: unsafeBrandId(reversePurposeSeed.consumerId), + title: reversePurposeSeed.title, + description: reversePurposeSeed.description, + isFreeOfCharge: reversePurposeSeed.isFreeOfCharge, + freeOfChargeReason: reversePurposeSeed.freeOfChargeReason, + riskAnalysisForm: mockRiskAnalysis.riskAnalysisForm, + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createPurposeFromEService( + producer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError(organizationIsNotTheConsumer(producer.id)); + }); + it("should throw eserviceModeNotAllowed if the eservice is in deliver mode", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.deliver, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError( + eServiceModeNotAllowed(mockEService.id, eserviceMode.receive) + ); + }); + it("should throw riskAnalysisNotFound if the selected riskAnalysis doesn't exist in that eservice", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const randomRiskAnalysisId: RiskAnalysisId = generateId(); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: randomRiskAnalysisId, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError( + eserviceRiskAnalysisNotFound(mockEService.id, randomRiskAnalysisId) + ); + }); + it("should throw missingFreeOfChargeReason if freeOfChargeReason has been omitted", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError(missingFreeOfChargeReason()); + }); + it("should throw tenantKindNotFound if the tenant kind doesn't exist", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: undefined }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError(tenantKindNotFound(producer.id)); + }); + it("should throw agreementNotFound if the requester doesn't have an agreement for the selected eservice", () => { + expect(1).toBe(1); + }); + it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", () => { + expect(2).toBe(2); + }); + }); From 360358c4e623a47f9c8bfab2d19498a3d78911bc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:12:45 +0200 Subject: [PATCH 322/537] Fix --- packages/purpose-process/src/model/domain/errors.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 2a012c8737..30b4694386 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -213,16 +213,6 @@ export function duplicatedPurposeName(title: string): ApiError { }); } -export function purposeCannotBeDeleted( - purposeId: PurposeId -): ApiError { - return new ApiError({ - detail: `Purpose ${purposeId} cannot be deleted`, - code: "purposeCannotBeDeleted", - title: "Purpose canont be deleted", - }); -} - export function eserviceRiskAnalysisNotFound( eserviceId: EServiceId, riskAnalysisId: RiskAnalysisId From ca70a61e6c1af9c682e72b2cd443427143712eb1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:13:12 +0200 Subject: [PATCH 323/537] Fix --- packages/commons-test/src/testUtils.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index a80997620a..256fbb5552 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -64,25 +64,6 @@ export const getMockDescriptorPublished = ( }, }); -export const getMockDescriptor = (): Descriptor => ({ - id: generateId(), - version: "1", - docs: [], - state: descriptorState.draft, - audience: [], - voucherLifespan: 60, - dailyCallsPerConsumer: 10, - dailyCallsTotal: 1000, - createdAt: new Date(), - serverUrls: ["pagopa.it"], - agreementApprovalPolicy: "Automatic", - attributes: { - certified: [], - verified: [], - declared: [], - }, -}); - export const getMockDocument = (): Document => ({ name: "fileName", path: "filePath", From e23f4d610e4ddb77a8825a56f4bb6d487ea5ddf1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:14:18 +0200 Subject: [PATCH 324/537] Minor change --- packages/commons-test/src/testUtils.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 256fbb5552..c19ead438a 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -64,16 +64,6 @@ export const getMockDescriptorPublished = ( }, }); -export const getMockDocument = (): Document => ({ - name: "fileName", - path: "filePath", - id: generateId(), - prettyName: "prettyName", - contentType: "json", - checksum: "checksum", - uploadDate: new Date(), -}); - export const getMockEServiceAttribute = ( attributeId: AttributeId = generateId() ): EServiceAttribute => ({ @@ -240,3 +230,13 @@ export const getMockDescriptor = (): Descriptor => ({ declared: [], }, }); + +export const getMockDocument = (): Document => ({ + name: "fileName", + path: "filePath", + id: generateId(), + prettyName: "prettyName", + contentType: "json", + checksum: "checksum", + uploadDate: new Date(), +}); From b8c8d71e7b6d7c4b5ac333aaba43283be35dc224 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:25:44 +0200 Subject: [PATCH 325/537] Add tests --- .../test/testCreateReversePurpose.ts | 108 +++++++++++++++++- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index 12859a6450..1684c43e76 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -29,10 +29,12 @@ import { } from "pagopa-interop-models"; import { ApiReversePurposeSeed } from "../src/model/domain/models.js"; import { + agreementNotFound, eServiceModeNotAllowed, eserviceRiskAnalysisNotFound, missingFreeOfChargeReason, organizationIsNotTheConsumer, + riskAnalysisValidationFailed, tenantKindNotFound, } from "../src/model/domain/errors.js"; import { getMockEService } from "./utils.js"; @@ -392,10 +394,108 @@ export const testCreateReversePurpose = (): ReturnType => ) ).rejects.toThrowError(tenantKindNotFound(producer.id)); }); - it("should throw agreementNotFound if the requester doesn't have an agreement for the selected eservice", () => { - expect(1).toBe(1); + it("should throw agreementNotFound if the requester doesn't have an agreement for the selected eservice", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + + expect( + purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError(agreementNotFound(mockEService.id, consumer.id)); }); - it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", () => { - expect(2).toBe(2); + it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = { + ...getMockValidRiskAnalysis(tenantKind.PA), + newField: "unrequested", + }; + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: "test purpose title", + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createPurposeFromEService( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError(riskAnalysisValidationFailed([])); }); }); From 53feaa69b7aab68ce7f987a84bec87f628a21d21 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 23 Apr 2024 17:44:08 +0200 Subject: [PATCH 326/537] Fix test --- .../test/testCreateReversePurpose.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index 1684c43e76..f976d848c1 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -16,6 +16,7 @@ import { EService, Purpose, PurposeAddedV2, + RiskAnalysis, RiskAnalysisId, Tenant, agreementState, @@ -27,6 +28,7 @@ import { toReadModelEService, unsafeBrandId, } from "pagopa-interop-models"; +import { unexpectedRulesVersionError } from "pagopa-interop-commons"; import { ApiReversePurposeSeed } from "../src/model/domain/models.js"; import { agreementNotFound, @@ -47,7 +49,7 @@ import { } from "./purposeService.integration.test.js"; export const testCreateReversePurpose = (): ReturnType => - describe("createReveresePurpose", () => { + describe("createReversePurpose", () => { it("should write in event-store for the creation of a reverse purpose", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); @@ -414,13 +416,6 @@ export const testCreateReversePurpose = (): ReturnType => mode: eserviceMode.receive, }; - const mockAgreement: Agreement = { - ...getMockAgreement(), - eserviceId: mockEService.id, - consumerId: consumer.id, - state: agreementState.active, - }; - const reversePurposeSeed: ApiReversePurposeSeed = { eServiceId: mockEService.id, consumerId: consumer.id, @@ -455,9 +450,14 @@ export const testCreateReversePurpose = (): ReturnType => interface: getMockDocument(), }; - const mockRiskAnalysis = { - ...getMockValidRiskAnalysis(tenantKind.PA), - newField: "unrequested", + const validRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + + const mockRiskAnalysis: RiskAnalysis = { + ...validRiskAnalysis, + riskAnalysisForm: { + ...validRiskAnalysis.riskAnalysisForm, + version: "7", + }, }; const mockEService: EService = { ...getMockEService(), @@ -496,6 +496,12 @@ export const testCreateReversePurpose = (): ReturnType => reversePurposeSeed, generateId() ) - ).rejects.toThrowError(riskAnalysisValidationFailed([])); + ).rejects.toThrowError( + riskAnalysisValidationFailed([ + unexpectedRulesVersionError( + mockRiskAnalysis.riskAnalysisForm.version + ), + ]) + ); }); }); From 2590efb41ea0409d088a879d945b914521bed474 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 08:45:09 +0200 Subject: [PATCH 327/537] Fix typo --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 06c7547f38..ef35eb2a45 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -647,7 +647,7 @@ export function purposeServiceBuilder( correlationId: string ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info( - `Creating Purposes for EService ${seed.eServiceId}, Consumer ${seed.consumerId}` + `Creating Purpose for EService ${seed.eServiceId}, Consumer ${seed.consumerId}` ); const eserviceId: EServiceId = unsafeBrandId(seed.eServiceId); const consumerId: TenantId = unsafeBrandId(seed.consumerId); From 13a74e548850dabdb0830c9e646f6eeebb96907b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:40:17 +0200 Subject: [PATCH 328/537] Group handlers in error mapper --- .../src/utilities/errorMappers.ts | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index df649a531c..486e8c0d09 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -106,14 +106,20 @@ export const createPurposeFromEServiceErrorMapper = ( error: ApiError ): number => match(error.code) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) - .with("eserviceNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("eServiceModeNotAllowed", () => HTTP_STATUS_BAD_REQUEST) - .with("eserviceRiskAnalysisNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("missingFreeOfChargeReason", () => HTTP_STATUS_BAD_REQUEST) - .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) - .with("tenantKindNotFound", () => HTTP_STATUS_FORBIDDEN) - .with("agreementNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with( + "organizationIsNotTheConsumer", + "tenantKindNotFound", + () => HTTP_STATUS_FORBIDDEN + ) + .with( + "eserviceNotFound", + "eServiceModeNotAllowed", + "eserviceRiskAnalysisNotFound", + "missingFreeOfChargeReason", + "agreementNotFound", + "riskAnalysisValidationFailed", + () => HTTP_STATUS_BAD_REQUEST + ) .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From c53a8dde0f4b73790a60f569c5a86b5ef837c944 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:43:10 +0200 Subject: [PATCH 329/537] Group handlers in error mapper --- .../purpose-process/src/utilities/errorMappers.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index e28e27c418..f5034c1c9e 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -9,8 +9,11 @@ const { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_NOT_FOUND } = constants; export const getPurposeErrorMapper = (error: ApiError): number => match(error.code) - .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("eserviceNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) + .with( + "purposeNotFound", + "tenantNotFound", + "tenantKindNotFound", + () => HTTP_STATUS_NOT_FOUND + ) + .with("eserviceNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 98fd7466e86186735cf6e6b0cf70daced0adb3d4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:44:52 +0200 Subject: [PATCH 330/537] Group handlers in error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 85875816a2..561c7b4606 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -23,8 +23,11 @@ export const getRiskAnalysisDocumentErrorMapper = ( error: ApiError ): number => match(error.code) - .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with( + "purposeNotFound", + "purposeVersionNotFound", + "purposeVersionDocumentNotFound", + () => HTTP_STATUS_NOT_FOUND + ) .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) - .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("purposeVersionDocumentNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 3b3c548294fc8cd9b99d81990df0af228148d089 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:45:33 +0200 Subject: [PATCH 331/537] Group handlers in error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index ffdc7719cb..888224bc56 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -34,8 +34,11 @@ export const deletePurposeVersionErrorMapper = ( error: ApiError ): number => match(error.code) - .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with( + "purposeNotFound", + "purposeVersionNotFound", + () => HTTP_STATUS_NOT_FOUND + ) .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("purposeVersionCannotBeDeleted", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From ceb48bfe060a21f4c04cae21b5fba9ee3eb00b2a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:46:09 +0200 Subject: [PATCH 332/537] Group handlers in error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 6fea620d76..3afe744daf 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -45,8 +45,11 @@ export const rejectPurposeVersionErrorMapper = ( error: ApiError ): number => match(error.code) - .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with( + "purposeNotFound", + "purposeVersionNotFound", + () => HTTP_STATUS_NOT_FOUND + ) .with("organizationIsNotTheProducer", () => HTTP_STATUS_FORBIDDEN) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 143496a76064d1f66af156bc20f146d0ee6c5550 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:46:51 +0200 Subject: [PATCH 333/537] Group handlers in error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 87f891a12d..3bbdcc6830 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -75,8 +75,11 @@ export const archivePurposeVersionErrorMapper = ( error: ApiError ): number => match(error.code) - .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with( + "purposeNotFound", + "purposeVersionNotFound", + () => HTTP_STATUS_NOT_FOUND + ) .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 6cb938386ef7730caec543c57902020c95e9cd29 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 09:47:21 +0200 Subject: [PATCH 334/537] Group handlers in error mapper --- packages/purpose-process/src/utilities/errorMappers.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 2ba2aff55a..e9470f81da 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -85,8 +85,11 @@ export const suspendPurposeVersionErrorMapper = ( error: ApiError ): number => match(error.code) - .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("purposeVersionNotFound", () => HTTP_STATUS_NOT_FOUND) + .with( + "purposeNotFound", + "purposeVersionNotFound", + () => HTTP_STATUS_NOT_FOUND + ) .with("organizationNotAllowed", () => HTTP_STATUS_FORBIDDEN) .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 1c31364e8eac98f07104d1ecaf3c18917bff3d82 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 24 Apr 2024 10:13:27 +0200 Subject: [PATCH 335/537] replaced eserviceMode with const --- .../purpose-process/src/services/purposeService.ts | 11 ++++++----- packages/purpose-process/test/testUpdatePurpose.ts | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index cde5dbd41d..2d8d31e409 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -24,6 +24,7 @@ import { PurposeRiskAnalysisForm, PurposeEvent, EServiceMode, + eserviceMode, } from "pagopa-interop-models"; import { eserviceNotFound, @@ -299,7 +300,7 @@ export function purposeServiceBuilder( purposeId, purposeUpdateContent, organizationId, - "Deliver", + eserviceMode.deliver, { readModelService, correlationId, repository } ); }, @@ -398,7 +399,7 @@ const getInvolvedTenantByEServiceMode = async ( consumerId: TenantId, readModelService: ReadModelService ): Promise => { - if (eservice.mode === "Deliver") { + if (eservice.mode === eserviceMode.deliver) { return retrieveTenant(consumerId, readModelService); } else { return retrieveTenant(eservice.producerId, readModelService); @@ -409,7 +410,7 @@ const updatePurposeInternal = async ( purposeId: PurposeId, updateContent: ApiPurposeUpdateContent | ApiReversePurposeUpdateContent, organizationId: TenantId, - eserviceMode: EServiceMode, + mode: EServiceMode, { readModelService, correlationId, @@ -430,7 +431,7 @@ const updatePurposeInternal = async ( purpose.data.eserviceId, readModelService ); - assertEserviceHasSpecificMode(eservice, eserviceMode); + assertEserviceHasSpecificMode(eservice, mode); assertConsistentFreeOfCharge( updateContent.isFreeOfCharge, updateContent.freeOfChargeReason @@ -445,7 +446,7 @@ const updatePurposeInternal = async ( assertTenantKindExists(tenant); const newRiskAnalysis: PurposeRiskAnalysisForm | undefined = - eserviceMode === "Deliver" + mode === eserviceMode.deliver ? validateAndTransformRiskAnalysis( (updateContent as ApiPurposeUpdateContent).riskAnalysisForm, tenant.kind diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts index 62c751edef..f931c78897 100644 --- a/packages/purpose-process/test/testUpdatePurpose.ts +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -26,6 +26,7 @@ import { TenantId, EServiceId, RiskAnalysis, + eserviceMode, } from "pagopa-interop-models"; import { describe, it, expect } from "vitest"; import { @@ -67,12 +68,12 @@ export const testUpdatePurpose = (): ReturnType => const eServiceDeliver: EService = { ...getMockEService(), - mode: "Deliver", + mode: eserviceMode.deliver, }; const eServiceReceive: EService = { ...getMockEService(), - mode: "Receive", + mode: eserviceMode.receive, producerId: tenant.id, }; From 42bd748435344384987991e9c385c1e4fd7f2453 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 11:44:53 +0200 Subject: [PATCH 336/537] Add to do --- packages/purpose-process/src/routers/PurposeRouter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 88b5f21440..eb4c89286b 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -324,6 +324,7 @@ const purposeRouter = ( .post( "/purposes/:purposeId/clone", authorizationMiddleware([ADMIN_ROLE]), + // TO DO (_req, res) => res.status(501).send() ) .post( From 743cf59adf734e16c9a0b893855136aaa9a0089f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 24 Apr 2024 16:29:11 +0200 Subject: [PATCH 337/537] Update error --- packages/purpose-process/src/model/domain/errors.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 0a20abb3aa..f551230456 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -193,8 +193,8 @@ export function purposeCannotBeDeleted( purposeId: PurposeId ): ApiError { return new ApiError({ - detail: `Purpose ${purposeId} cannot be deleted`, + detail: `Versions in Purpose ${purposeId} do not allow deletion`, code: "purposeCannotBeDeleted", - title: "Purpose canont be deleted", + title: "Purpose cannot be deleted", }); } From 39d7f186476e636bf473b84f46d50df6a874b69c Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Mon, 29 Apr 2024 11:35:34 +0200 Subject: [PATCH 338/537] fix as suggest --- .../src/model/domain/errors.ts | 14 +---- .../src/services/purposeService.ts | 15 ++---- .../src/services/readModelService.ts | 29 +---------- .../src/utilities/errorMappers.ts | 9 ++-- .../purpose-process/test/testCreatePurpose.ts | 51 ++----------------- 5 files changed, 13 insertions(+), 105 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 2e77912b21..bd55919e34 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -29,8 +29,7 @@ export const errorCodes = { riskAnalysisValidationFailed: "0015", purposeNotInDraftState: "0016", purposeCannotBeDeleted: "0017", - agreementNotFound: "0018", - duplicatedPurposeName: "0019", + duplicatedPurposeName: "0018", }; export type ErrorCodes = keyof typeof errorCodes; @@ -201,17 +200,6 @@ export function purposeCannotBeDeleted( }); } -export function agreementNotFound( - eserviceId: EServiceId, - consumerId: TenantId -): ApiError { - return new ApiError({ - detail: `No Agreement found for EService ${eserviceId} and Consumer ${consumerId}`, - code: "agreementNotFound", - title: "Agreement Not Found", - }); -} - export function duplicatedPurposeName(title: string): ApiError { return new ApiError({ detail: `Purpose with name: ${title} already exists`, diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 07202b28f2..ff364af149 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -30,7 +30,6 @@ import { } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { - agreementNotFound, duplicatedPurposeName, eserviceNotFound, missingRejectionReason, @@ -588,15 +587,6 @@ export function purposeServiceBuilder( tenant.kind ); - const agreement = await readModelService.getActiveAgreement( - eserviceId, - consumerId - ); - - if (agreement === undefined) { - throw agreementNotFound(eserviceId, consumerId); - } - const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, consumerId, @@ -620,8 +610,9 @@ export function purposeServiceBuilder( riskAnalysisForm: validatedFormSeed, }; - const event = toCreateEventPurposeAdded(purpose, correlationId); - await repository.createEvent(event); + await repository.createEvent( + toCreateEventPurposeAdded(purpose, correlationId) + ); return { purpose, isRiskAnalysisValid: validatedFormSeed !== undefined }; }, }; diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 5a29f3bd1c..a978100140 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -18,8 +18,6 @@ import { PurposeId, ListResult, purposeVersionState, - Agreement, - agreementState, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -103,7 +101,7 @@ async function getTenant( export function readModelServiceBuilder( readModelRepository: ReadModelRepository ) { - const { eservices, purposes, tenants, agreements } = readModelRepository; + const { eservices, purposes, tenants } = readModelRepository; return { async getEServiceById(id: EServiceId): Promise { @@ -131,30 +129,7 @@ export function readModelServiceBuilder( }, } satisfies ReadModelFilter); }, - async getActiveAgreement( - eserviceId: EServiceId, - consumerId: TenantId - ): Promise { - const data = await agreements.findOne({ - "data.eserviceId": eserviceId, - "data.consumerId": consumerId, - "data.state": agreementState.active, - }); - if (!data) { - return undefined; - } else { - const result = Agreement.safeParse(data.data); - if (!result.success) { - logger.error( - `Unable to parse agreement item: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - throw genericError("Unable to parse agreement item"); - } - return result.data; - } - }, + async getPurposes( filters: ApiGetPurposesFilters, offset: number, diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 32b2cc17b2..84bf4f8f56 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -98,9 +98,8 @@ export const createPurposeErrorMapper = (error: ApiError): number => match(error.code) .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("missingFreeOfChargeReason", () => HTTP_STATUS_NOT_FOUND) - .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("tenantKindNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_FORBIDDEN) - .with("agreementNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("duplicatedPurposeName", () => HTTP_STATUS_FORBIDDEN) + .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts index 12c3f05580..d881cdb9b0 100644 --- a/packages/purpose-process/test/testCreatePurpose.ts +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -2,7 +2,6 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { fail } from "assert"; import { - Agreement, Descriptor, EService, Purpose, @@ -33,7 +32,6 @@ import { missingFreeOfChargeReason, tenantKindNotFound, tenantNotFound, - agreementNotFound, organizationIsNotTheConsumer, riskAnalysisValidationFailed, duplicatedPurposeName, @@ -69,8 +67,6 @@ export const testCreatePurpose = (): ReturnType => const eService1: EService = { ...getMockEService(), producerId: tenant.id, - id: generateId(), - name: "A", descriptors: [descriptor1], }; @@ -219,47 +215,6 @@ export const testCreatePurpose = (): ReturnType => ) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); - it("should throw agreementNotFound", async () => { - const descriptor: Descriptor = { - ...descriptor1, - id: generateId(), - }; - - const eService: EService = { - ...eService1, - producerId: tenant.id, - id: generateId(), - descriptors: [descriptor], - }; - - const agreement: Agreement = { - ...agreementEservice1, - id: generateId(), - eserviceId: eService.id, - descriptorId: descriptor.id, - producerId: eService.producerId, - consumerId: tenant.id, - state: agreementState.draft, - }; - - const seed: ApiPurposeSeed = { - ...purposeSeed, - eserviceId: eService.id, - consumerId: agreement.consumerId, - }; - - await writeInReadmodel(tenant, tenants); - await writeInReadmodel(agreement, agreements); - await writeInReadmodel(toReadModelEService(eService), eservices); - - expect( - purposeService.createPurpose( - seed, - unsafeBrandId(seed.consumerId), - generateId() - ) - ).rejects.toThrowError(agreementNotFound(eService.id, tenant.id)); - }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { await writeInReadmodel(tenant, tenants); await writeInReadmodel(agreementEservice1, agreements); @@ -280,14 +235,14 @@ export const testCreatePurpose = (): ReturnType => organizationIsNotTheConsumer(unsafeBrandId(purposeSeed.consumerId)) ); }); - it("should throw riskAnalysisValidationFailed if the purpose has an inactive risk analysis ", async () => { + it("should throw riskAnalysisValidationFailed if the purpose has a non valid risk analysis ", async () => { await writeInReadmodel(tenant, tenants); await writeInReadmodel(agreementEservice1, agreements); await writeInReadmodel(toReadModelEService(eService1), eservices); const mockInvalidRiskAnalysisForm: RiskAnalysisForm = { ...mockValidRiskAnalysisForm, - version: "251", + version: "0", }; const seed: ApiPurposeSeed = { @@ -309,7 +264,7 @@ export const testCreatePurpose = (): ReturnType => ]) ); }); - it("should throw duplicatedPurposeName if exist alreay a purpose with same name", async () => { + it("should throw duplicatedPurposeName if a purpose with same name alreay exist", async () => { const existingPurpose: Purpose = { ...getMockPurpose(), eserviceId: unsafeBrandId(purposeSeed.eserviceId), From 7f1029dd386b6e9f7d03eddd1d8597619aaed31e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:09:16 +0200 Subject: [PATCH 339/537] Fix and refactor --- packages/purpose-process/src/services/purposeService.ts | 8 +++----- packages/purpose-process/src/services/validators.ts | 8 ++++++++ .../purpose-process/test/testArchivePurposeVersion.ts | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ae494b473c..f665651ca6 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -62,6 +62,7 @@ import { validateAndTransformRiskAnalysis, assertPurposeIsDraft, assertPurposeIsDeletable, + isArchivable, } from "./validators.js"; const retrievePurpose = async ( @@ -376,10 +377,7 @@ export function purposeServiceBuilder( assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); const purposeVersion = retrievePurposeVersion(versionId, purpose); - if ( - purposeVersion.state !== purposeVersionState.active && - purposeVersion.state !== purposeVersionState.suspended - ) { + if (!isArchivable(purposeVersion)) { throw notValidVersionState(versionId, purposeVersion.state); } @@ -391,7 +389,7 @@ export function purposeServiceBuilder( }; const archivedVersion: PurposeVersion = { ...purposeVersion, - state: purposeVersionState.rejected, + state: purposeVersionState.archived, updatedAt: new Date(), }; const updatedPurpose = replacePurposeVersion( diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 64fa0eaabf..0599185406 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -3,6 +3,7 @@ import { EServiceMode, Purpose, PurposeRiskAnalysisForm, + PurposeVersion, RiskAnalysisForm, Tenant, TenantId, @@ -151,3 +152,10 @@ export function assertPurposeIsDeletable(purpose: Purpose): void { throw purposeCannotBeDeleted(purpose.id); } } + +export function isArchivable(purposeVersion: PurposeVersion): boolean { + return ( + purposeVersion.state === purposeVersionState.active || + purposeVersion.state === purposeVersionState.suspended + ); +} diff --git a/packages/purpose-process/test/testArchivePurposeVersion.ts b/packages/purpose-process/test/testArchivePurposeVersion.ts index c074e381d8..706230cdfe 100644 --- a/packages/purpose-process/test/testArchivePurposeVersion.ts +++ b/packages/purpose-process/test/testArchivePurposeVersion.ts @@ -71,7 +71,7 @@ export const testArchivePurposeVersion = (): ReturnType => versions: [ { ...mockPurposeVersion, - state: purposeVersionState.rejected, + state: purposeVersionState.archived, updatedAt: new Date(), }, ], @@ -130,7 +130,7 @@ export const testArchivePurposeVersion = (): ReturnType => versions: [ { ...mockPurposeVersion1, - state: purposeVersionState.rejected, + state: purposeVersionState.archived, updatedAt: new Date(), }, ], From e9b342468c20d128f4871007856f08bc74dd90fe Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:16:04 +0200 Subject: [PATCH 340/537] Refactor --- packages/purpose-process/src/services/purposeService.ts | 6 ++---- packages/purpose-process/src/services/validators.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 69a49f4632..92c21e0fe2 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -66,6 +66,7 @@ import { assertPurposeIsDraft, assertPurposeIsDeletable, isArchivable, + isSuspendable, } from "./validators.js"; const retrievePurpose = async ( @@ -439,10 +440,7 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); - if ( - purposeVersion.state !== purposeVersionState.active && - purposeVersion.state !== purposeVersionState.suspended - ) { + if (!isSuspendable(purposeVersion)) { throw notValidVersionState(purposeVersion.id, purposeVersion.state); } diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 0599185406..bfb31a52f9 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -159,3 +159,11 @@ export function isArchivable(purposeVersion: PurposeVersion): boolean { purposeVersion.state === purposeVersionState.suspended ); } + +// eslint-disable-next-line sonarjs/no-identical-functions +export function isSuspendable(purposeVersion: PurposeVersion): boolean { + return ( + purposeVersion.state === purposeVersionState.active || + purposeVersion.state === purposeVersionState.suspended + ); +} From 29805e242bd72a58f1e21394d49a1dc2698d00e1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:21:31 +0200 Subject: [PATCH 341/537] Fix logic --- .../src/services/purposeService.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 92c21e0fe2..5a47e8df35 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -464,7 +464,7 @@ export function purposeServiceBuilder( correlationId, }); }) - .with(ownership.PRODUCER, () => { + .with(ownership.PRODUCER, ownership.SELF_CONSUMER, () => { const updatedPurpose: Purpose = { ...replacePurposeVersion(purpose.data, suspendedPurposeVersion), suspendedByProducer: true, @@ -476,19 +476,6 @@ export function purposeServiceBuilder( correlationId, }); }) - .with(ownership.SELF_CONSUMER, () => { - const updatedPurpose: Purpose = { - ...replacePurposeVersion(purpose.data, suspendedPurposeVersion), - suspendedByConsumer: true, - suspendedByProducer: true, - }; - return toCreateEventPurposeSuspendedByConsumer({ - purpose: updatedPurpose, - purposeVersionId: versionId, - version: purpose.metadata.version, - correlationId, - }); - }) .exhaustive(); await repository.createEvent(event); From c05d8c023d91581bf1230abe2f0dfa046d53a54c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:21:35 +0200 Subject: [PATCH 342/537] Add test --- .../test/testSuspendPurposeVersion.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/packages/purpose-process/test/testSuspendPurposeVersion.ts b/packages/purpose-process/test/testSuspendPurposeVersion.ts index bb6a1e37e9..8329822556 100644 --- a/packages/purpose-process/test/testSuspendPurposeVersion.ts +++ b/packages/purpose-process/test/testSuspendPurposeVersion.ts @@ -156,6 +156,67 @@ export const testSuspendPurposeVersion = (): ReturnType => vi.useRealTimers(); }); + it("should write on event-store for the suspension of a purpose version by the producer (self consumer)", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockEService = getMockEService(); + const mockPurposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockEService.producerId, + versions: [mockPurposeVersion1], + }; + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + await purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeVersionSuspendedByProducer", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion1, + state: purposeVersionState.suspended, + suspendedAt: new Date(), + updatedAt: new Date(), + }, + ], + suspendedByProducer: true, + updatedAt: new Date(), + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionSuspendedByProducerV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const randomPurposeId: PurposeId = generateId(); const randomVersionId: PurposeVersionId = generateId(); From 97668faa3960727b166642931d8c0e101c82c161 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:27:44 +0200 Subject: [PATCH 343/537] Minor change in test --- .../purpose-process/test/testGetRiskAnalysisDocument.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts index e258c3d6bd..b34be4928c 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts @@ -43,11 +43,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - const mockPurpose2: Purpose = { - ...getMockPurpose(), - id: generateId(), - title: "another purpose", - }; + const mockPurpose2 = getMockPurpose(); await addOnePurpose(mockPurpose1, postgresDB, purposes); await addOnePurpose(mockPurpose2, postgresDB, purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); From 5e526863f27335afe458d4f837082086db7b5fcb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:32:09 +0200 Subject: [PATCH 344/537] Add missing error handler --- packages/purpose-process/src/utilities/errorMappers.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 1668dda849..3f7998a9fe 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -60,5 +60,9 @@ export const rejectPurposeVersionErrorMapper = ( () => HTTP_STATUS_NOT_FOUND ) .with("organizationIsNotTheProducer", () => HTTP_STATUS_FORBIDDEN) - .with("notValidVersionState", () => HTTP_STATUS_BAD_REQUEST) + .with( + "notValidVersionState", + "missingRejectionReason", + () => HTTP_STATUS_BAD_REQUEST + ) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 3b0b72ce2df59c241c0c3a788c92a4e439c99cd8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:35:57 +0200 Subject: [PATCH 345/537] Refactor --- .../src/services/purposeService.ts | 16 ++++++++++------ .../purpose-process/src/services/validators.ts | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b587d97506..57f324dadc 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -36,7 +36,11 @@ import { toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { ReadModelService } from "./readModelService.js"; -import { isRiskAnalysisFormValid, purposeIsDraft } from "./validators.js"; +import { + isRejectable, + isRiskAnalysisFormValid, + purposeIsDraft, +} from "./validators.js"; const retrievePurpose = async ( purposeId: PurposeId, @@ -224,6 +228,10 @@ export function purposeServiceBuilder( }): Promise { logger.info(`Rejecting Version ${versionId} in Purpose ${purposeId}`); + if (!rejectionReason) { + throw missingRejectionReason(); + } + const purpose = await retrievePurpose(purposeId, readModelService); const eservice = await retrieveEService( purpose.data.eserviceId, @@ -235,14 +243,10 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); - if (purposeVersion.state !== purposeVersionState.waitingForApproval) { + if (!isRejectable(purposeVersion)) { throw notValidVersionState(purposeVersion.id, purposeVersion.state); } - if (!rejectionReason) { - throw missingRejectionReason(); - } - const updatedPurposeVersion: PurposeVersion = { ...purposeVersion, state: purposeVersionState.rejected, diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 4c9c595753..160c01a25e 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -4,6 +4,7 @@ import { } from "pagopa-interop-commons"; import { Purpose, + PurposeVersion, RiskAnalysisForm, TenantKind, purposeVersionState, @@ -29,3 +30,6 @@ export const isRiskAnalysisFormValid = ( export const purposeIsDraft = (purpose: Purpose): boolean => !purpose.versions.some((v) => v.state !== purposeVersionState.draft); + +export const isRejectable = (purposeVersion: PurposeVersion): boolean => + purposeVersion.state === purposeVersionState.waitingForApproval; From 215b7b3d83b5a091f1a2859026e68e583390b8fa Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:45:52 +0200 Subject: [PATCH 346/537] Minor refactor --- packages/purpose-process/src/services/validators.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 0599185406..5a4d9d1f64 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -153,9 +153,6 @@ export function assertPurposeIsDeletable(purpose: Purpose): void { } } -export function isArchivable(purposeVersion: PurposeVersion): boolean { - return ( - purposeVersion.state === purposeVersionState.active || - purposeVersion.state === purposeVersionState.suspended - ); -} +export const isArchivable = (purposeVersion: PurposeVersion): boolean => + purposeVersion.state === purposeVersionState.active || + purposeVersion.state === purposeVersionState.suspended; From d6a88b1c0687977ec4f4f16756366a3e497f0496 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 12:47:12 +0200 Subject: [PATCH 347/537] Minor refactor --- packages/purpose-process/src/services/validators.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 72afcedf5b..2f4c1cb1ae 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -157,10 +157,6 @@ export const isArchivable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.active || purposeVersion.state === purposeVersionState.suspended; -// eslint-disable-next-line sonarjs/no-identical-functions -export function isSuspendable(purposeVersion: PurposeVersion): boolean { - return ( - purposeVersion.state === purposeVersionState.active || - purposeVersion.state === purposeVersionState.suspended - ); -} +export const isSuspendable = (purposeVersion: PurposeVersion): boolean => + purposeVersion.state === purposeVersionState.active || + purposeVersion.state === purposeVersionState.suspended; From e3c649b9589d849b621ab158e79ae30c934e2fa2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 17:26:30 +0200 Subject: [PATCH 348/537] Add type for seed --- packages/purpose-process/src/model/domain/models.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 0bd9f1310f..eda4a8901d 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -42,3 +42,5 @@ export type ApiGetPurposesFilters = { export type ApiReversePurposeSeed = z.infer< typeof api.schemas.EServicePurposeSeed >; + +export type ApiPurposeCloneSeed = z.infer; From e3ea750207650c1ce11743638628fe7293dfde5e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 17:26:44 +0200 Subject: [PATCH 349/537] Add event wrapper --- .../src/model/domain/toEvent.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index daf92445d0..674c94ae9b 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -2,6 +2,7 @@ import { CreateEvent } from "pagopa-interop-commons"; import { Purpose, PurposeEvent, + PurposeId, PurposeVersionId, toPurposeV2, } from "pagopa-interop-models"; @@ -187,3 +188,24 @@ export function toCreateEventPurposeAdded( correlationId, }; } + +export const toCreateEventPurposeCloned = ({ + purpose, + sourcePurposeId, + sourceVersionId, + correlationId, +}: { + purpose: Purpose; + sourcePurposeId: PurposeId; + sourceVersionId: PurposeId; + correlationId: string; +}): CreateEvent => ({ + streamId: purpose.id, + version: 0, + event: { + type: "PurposeCloned", + event_version: 2, + data: { purpose: toPurposeV2(purpose), sourcePurposeId, sourceVersionId }, + }, + correlationId, +}); From 73c1e0cf7d3f482ce245caf3ccda944b9a5b90ea Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 17:26:54 +0200 Subject: [PATCH 350/537] Add error --- packages/purpose-process/src/model/domain/errors.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 30b4694386..be6ab39cea 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -32,6 +32,7 @@ export const errorCodes = { agreementNotFound: "0017", duplicatedPurposeName: "0018", eserviceRiskAnalysisNotFound: "0019", + purposeCannotBeCloned: "0020", }; export type ErrorCodes = keyof typeof errorCodes; @@ -223,3 +224,13 @@ export function eserviceRiskAnalysisNotFound( title: "Risk analysis not found", }); } + +export function purposeCannotBeCloned( + purposeId: PurposeId +): ApiError { + return new ApiError({ + detail: `Purpose ${purposeId} cannot be cloned`, + code: "purposeCannotBeCloned", + title: "Purpose cannot be cloned", + }); +} From 74cb8dbf33795da5b327b31d4089730e96822ec1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 17:26:59 +0200 Subject: [PATCH 351/537] Add error mapper --- .../purpose-process/src/utilities/errorMappers.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 486e8c0d09..750deb2895 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -123,3 +123,16 @@ export const createPurposeFromEServiceErrorMapper = ( .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const clonePurposeErrorMapper = (error: ApiError): number => + match(error.code) + .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantKindNotFound", () => HTTP_STATUS_FORBIDDEN) + .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with( + "duplicatedPurposeName", + "purposeCannotBeCloned", + () => HTTP_STATUS_CONFLICT + ) + .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 51e4e42108f922b0c912e2af5242af60fdfd70eb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 17:27:42 +0200 Subject: [PATCH 352/537] Implement endpoint --- .../src/services/purposeService.ts | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ef35eb2a45..2bb91ccc47 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -43,6 +43,7 @@ import { organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, + purposeCannotBeCloned, purposeNotFound, purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, @@ -55,6 +56,7 @@ import { toCreateEventDraftPurposeUpdated, toCreateEventPurposeAdded, toCreateEventPurposeArchived, + toCreateEventPurposeCloned, toCreateEventPurposeSuspendedByConsumer, toCreateEventPurposeSuspendedByProducer, toCreateEventPurposeVersionRejected, @@ -67,6 +69,7 @@ import { ApiGetPurposesFilters, ApiPurposeSeed, ApiReversePurposeSeed, + ApiPurposeCloneSeed, } from "../model/domain/models.js"; import { ReadModelService } from "./readModelService.js"; import { @@ -724,6 +727,95 @@ export function purposeServiceBuilder( isRiskAnalysisValid: validationResult.type === "valid", }; }, + async clonePurpose({ + purposeId, + organizationId, + seed, + correlationId, + }: { + purposeId: PurposeId; + organizationId: TenantId; + seed: ApiPurposeCloneSeed; + correlationId: string; + }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { + logger.info(`Cloning Purpose ${purposeId}`); + + const tenant = await retrieveTenant(organizationId, readModelService); + const purposeToClone = await retrievePurpose(purposeId, readModelService); + + if (purposeIsDraft(purposeToClone.data)) { + throw purposeCannotBeCloned(purposeId); + } + + const dailyCalls = getDailyCallsFromPurposeToClone(purposeToClone.data); + + const newPurposeVersion: PurposeVersion = { + id: generateId(), + createdAt: new Date(), + state: purposeVersionState.draft, + dailyCalls, + }; + + const riskAnalysisFormToClone = purposeToClone.data.riskAnalysisForm; + + const clonedRiskAnalysisForm: PurposeRiskAnalysisForm | undefined = + riskAnalysisFormToClone + ? { + id: generateId(), + version: "1", // TO DO: "0"? + riskAnalysisId: riskAnalysisFormToClone.riskAnalysisId, // TO DO double-check + singleAnswers: riskAnalysisFormToClone.singleAnswers.map( + (answer) => ({ + ...answer, + id: generateId(), + }) + ), + multiAnswers: riskAnalysisFormToClone.multiAnswers.map( + (answer) => ({ + ...answer, + id: generateId(), + }) + ), + } + : undefined; + + const clonedPurpose: Purpose = { + title: purposeToClone.data.title, + id: generateId(), + createdAt: new Date(), + eserviceId: unsafeBrandId(seed.eserviceId), + consumerId: organizationId, + description: purposeToClone.data.description, + versions: [newPurposeVersion], + isFreeOfCharge: purposeToClone.data.isFreeOfCharge, + freeOfChargeReason: purposeToClone.data.freeOfChargeReason, + riskAnalysisForm: clonedRiskAnalysisForm, + }; + + assertTenantKindExists(tenant); + + const isRiskAnalysisValid = clonedRiskAnalysisForm + ? validateRiskAnalysis( + riskAnalysisFormToRiskAnalysisFormToValidate( + clonedRiskAnalysisForm + ), + false, + tenant.kind + ).type === "valid" + : false; + + const event = toCreateEventPurposeCloned({ + purpose: clonedPurpose, + sourcePurposeId: purposeToClone.data.id, + sourceVersionId: generateId(), // TO DO not sure where to take this, but the event definition requires it + correlationId, + }); + await repository.createEvent(event); + return { + purpose: clonedPurpose, + isRiskAnalysisValid, + }; + }, }; } @@ -879,3 +971,28 @@ const updatePurposeInternal = async ( ), }; }; + +const getDailyCallsFromPurposeToClone = (purposeToClone: Purpose): number => { + const nonWaitingVersions = purposeToClone.versions.filter( + (v) => v.state !== purposeVersionState.waitingForApproval + ); + + const versionsToSearch = + nonWaitingVersions.length > 0 + ? nonWaitingVersions + : purposeToClone.versions; + + const sortedVersions = versionsToSearch + .filter((v) => v.state !== purposeVersionState.waitingForApproval) + .sort((v1, v2) => { + if (v1.createdAt > v2.createdAt) { + return -1; + } else if (v1.createdAt < v2.createdAt) { + return 1; + } else { + return 0; + } + }); + + return sortedVersions.length > 0 ? sortedVersions[0].dailyCalls : 0; +}; From d23a78fec5298f0cf23330a96b7111a9545fe1ac Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 17:27:54 +0200 Subject: [PATCH 353/537] Update router --- .../src/routers/PurposeRouter.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index eb4c89286b..948d6ad444 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -22,6 +22,7 @@ import { purposeServiceBuilder } from "../services/purposeService.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { archivePurposeVersionErrorMapper, + clonePurposeErrorMapper, createPurposeErrorMapper, createPurposeFromEServiceErrorMapper, deletePurposeErrorMapper, @@ -324,8 +325,24 @@ const purposeRouter = ( .post( "/purposes/:purposeId/clone", authorizationMiddleware([ADMIN_ROLE]), - // TO DO - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const { purpose, isRiskAnalysisValid } = + await purposeService.clonePurpose({ + purposeId: unsafeBrandId(req.params.purposeId), + organizationId: req.ctx.authData.organizationId, + seed: req.body, + correlationId: req.ctx.correlationId, + }); + return res + .status(200) + .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) + .end(); + } catch (error) { + const errorRes = makeApiProblem(error, clonePurposeErrorMapper); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .post( "/purposes/:purposeId/versions/:versionId/suspend", From 439cced6fe4929e94ad87f67196890f412857d88 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 29 Apr 2024 18:06:59 +0200 Subject: [PATCH 354/537] Create draft version --- packages/purpose-process/src/services/purposeService.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index e003df46b1..88cfbfe9c5 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -701,6 +701,13 @@ export function purposeServiceBuilder( throw riskAnalysisValidationFailed(validationResult.issues); } + const newVersion: PurposeVersion = { + id: generateId(), + createdAt: new Date(), + state: purposeVersionState.draft, + dailyCalls: seed.dailyCalls, + }; + const purpose: Purpose = { title: seed.title, id: generateId(), @@ -708,7 +715,7 @@ export function purposeServiceBuilder( eserviceId, consumerId, description: seed.description, - versions: [], + versions: [newVersion], isFreeOfCharge: seed.isFreeOfCharge, freeOfChargeReason: seed.freeOfChargeReason, riskAnalysisForm: riskAnalysis.riskAnalysisForm, From 83a4f91419ff5589c09785e0a507327f16b6118c Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Mon, 29 Apr 2024 18:21:13 +0200 Subject: [PATCH 355/537] restored getActiveAgreement --- .../src/model/domain/errors.ts | 14 ++++- .../src/services/purposeService.ts | 33 ++++++++++-- .../src/services/readModelService.ts | 28 +++++++++- .../src/utilities/errorMappers.ts | 1 + .../purpose-process/test/testCreatePurpose.ts | 53 ++++++++++++++++++- 5 files changed, 121 insertions(+), 8 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index bd55919e34..2e77912b21 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -29,7 +29,8 @@ export const errorCodes = { riskAnalysisValidationFailed: "0015", purposeNotInDraftState: "0016", purposeCannotBeDeleted: "0017", - duplicatedPurposeName: "0018", + agreementNotFound: "0018", + duplicatedPurposeName: "0019", }; export type ErrorCodes = keyof typeof errorCodes; @@ -200,6 +201,17 @@ export function purposeCannotBeDeleted( }); } +export function agreementNotFound( + eserviceId: EServiceId, + consumerId: TenantId +): ApiError { + return new ApiError({ + detail: `No Agreement found for EService ${eserviceId} and Consumer ${consumerId}`, + code: "agreementNotFound", + title: "Agreement Not Found", + }); +} + export function duplicatedPurposeName(title: string): ApiError { return new ApiError({ detail: `Purpose with name: ${title} already exists`, diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ff364af149..a945b6808b 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -27,9 +27,11 @@ import { ListResult, unsafeBrandId, generateId, + Agreement, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { + agreementNotFound, duplicatedPurposeName, eserviceNotFound, missingRejectionReason, @@ -140,6 +142,21 @@ const retrieveTenant = async ( return tenant; }; +const retrieveAgreement = async ( + eserviceId: EServiceId, + consumerId: TenantId, + readModelService: ReadModelService +): Promise => { + const activeAgreement = await readModelService.getActiveAgreement( + eserviceId, + consumerId + ); + if (activeAgreement === undefined) { + throw agreementNotFound(eserviceId, consumerId); + } + return activeAgreement; +}; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( dbInstance: DB, @@ -587,6 +604,8 @@ export function purposeServiceBuilder( tenant.kind ); + await retrieveAgreement(eserviceId, consumerId, readModelService); + const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, consumerId, @@ -598,15 +617,19 @@ export function purposeServiceBuilder( } const purpose: Purpose = { - title: purposeSeed.title, + ...purposeSeed, id: generateId(), createdAt: new Date(), eserviceId, consumerId, - description: purposeSeed.description, - versions: [], - isFreeOfCharge: purposeSeed.isFreeOfCharge, - freeOfChargeReason: purposeSeed.freeOfChargeReason, + versions: [ + { + id: generateId(), + state: purposeVersionState.draft, + dailyCalls: purposeSeed.dailyCalls, + createdAt: new Date(), + }, + ], riskAnalysisForm: validatedFormSeed, }; diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index a978100140..55aeda4865 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -18,6 +18,8 @@ import { PurposeId, ListResult, purposeVersionState, + Agreement, + agreementState, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -101,7 +103,7 @@ async function getTenant( export function readModelServiceBuilder( readModelRepository: ReadModelRepository ) { - const { eservices, purposes, tenants } = readModelRepository; + const { eservices, purposes, tenants, agreements } = readModelRepository; return { async getEServiceById(id: EServiceId): Promise { @@ -251,6 +253,30 @@ export function readModelServiceBuilder( ), }; }, + async getActiveAgreement( + eserviceId: EServiceId, + consumerId: TenantId + ): Promise { + const data = await agreements.findOne({ + "data.eserviceId": eserviceId, + "data.consumerId": consumerId, + "data.state": agreementState.active, + }); + if (!data) { + return undefined; + } else { + const result = Agreement.safeParse(data.data); + if (!result.success) { + logger.error( + `Unable to parse agreement item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + throw genericError("Unable to parse agreement item"); + } + return result.data; + } + }, }; } diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 84bf4f8f56..f37e7049cd 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -98,6 +98,7 @@ export const createPurposeErrorMapper = (error: ApiError): number => match(error.code) .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with("missingFreeOfChargeReason", () => HTTP_STATUS_NOT_FOUND) + .with("agreementNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts index d881cdb9b0..305f560c79 100644 --- a/packages/purpose-process/test/testCreatePurpose.ts +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { fail } from "assert"; import { + Agreement, Descriptor, EService, Purpose, @@ -11,6 +12,7 @@ import { agreementState, descriptorState, generateId, + purposeVersionState, tenantKind, toPurposeV2, toReadModelEService, @@ -35,6 +37,7 @@ import { organizationIsNotTheConsumer, riskAnalysisValidationFailed, duplicatedPurposeName, + agreementNotFound, } from "../src/model/domain/errors.js"; import { ApiPurposeSeed } from "../src/model/domain/models.js"; import { @@ -149,7 +152,14 @@ export const testCreatePurpose = (): ReturnType => eserviceId: unsafeBrandId(purposeSeed.eserviceId), consumerId: unsafeBrandId(purposeSeed.consumerId), description: purposeSeed.description, - versions: [], + versions: [ + { + id: unsafeBrandId(writtenPayload.purpose!.versions[0].id), + state: purposeVersionState.draft, + dailyCalls: purposeSeed.dailyCalls, + createdAt: new Date(), + }, + ], isFreeOfCharge: true, freeOfChargeReason: purposeSeed.freeOfChargeReason, riskAnalysisForm: expectedRiskAnalysisForm, @@ -215,6 +225,47 @@ export const testCreatePurpose = (): ReturnType => ) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); + it("should throw agreementNotFound", async () => { + const descriptor: Descriptor = { + ...descriptor1, + id: generateId(), + }; + + const eService: EService = { + ...eService1, + producerId: tenant.id, + id: generateId(), + descriptors: [descriptor], + }; + + const agreement: Agreement = { + ...agreementEservice1, + id: generateId(), + eserviceId: eService.id, + descriptorId: descriptor.id, + producerId: eService.producerId, + consumerId: tenant.id, + state: agreementState.draft, + }; + + const seed: ApiPurposeSeed = { + ...purposeSeed, + eserviceId: eService.id, + consumerId: agreement.consumerId, + }; + + await writeInReadmodel(tenant, tenants); + await writeInReadmodel(agreement, agreements); + await writeInReadmodel(toReadModelEService(eService), eservices); + + expect( + purposeService.createPurpose( + seed, + unsafeBrandId(seed.consumerId), + generateId() + ) + ).rejects.toThrowError(agreementNotFound(eService.id, tenant.id)); + }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { await writeInReadmodel(tenant, tenants); await writeInReadmodel(agreementEservice1, agreements); From db3bbcea750422ebaacecdb38e09eb5c94677d9e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 09:21:57 +0200 Subject: [PATCH 356/537] Refactor --- packages/purpose-process/src/services/purposeService.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d3741296c2..e4b28ad029 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -693,14 +693,7 @@ export function purposeServiceBuilder( assertTenantKindExists(producer); - const agreement = await readModelService.getActiveAgreement( - eserviceId, - consumerId - ); - - if (agreement === undefined) { - throw agreementNotFound(eserviceId, consumerId); - } + await retrieveAgreement(eserviceId, consumerId, readModelService); const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, From 4868473d0d02bbe7444c32274e02986eae4a8b6e Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 09:49:57 +0200 Subject: [PATCH 357/537] fixed jwt test --- packages/commons-test/test/jwt.unit.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/commons-test/test/jwt.unit.test.ts b/packages/commons-test/test/jwt.unit.test.ts index 374106ef01..901f06feb9 100644 --- a/packages/commons-test/test/jwt.unit.test.ts +++ b/packages/commons-test/test/jwt.unit.test.ts @@ -172,12 +172,12 @@ describe("JWT tests", () => { ]); const token = getMockSignedToken({ ...mockToken, - sub: undefined, + iss: undefined, jti: undefined, }); expect(() => readAuthDataFromJwtToken(token)).toThrowError( - invalidClaim(`Validation error: Required at "jti"; Required at "sub"`) + invalidClaim(`Validation error: Required at "iss"; Required at "jti"`) ); }); From acc00ef0352816488ccaa18db2a5424c4dd62486 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 11:16:01 +0200 Subject: [PATCH 358/537] Fix test --- .../purpose-process/test/testCreateReversePurpose.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index f976d848c1..870b4e386d 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -23,6 +23,7 @@ import { descriptorState, eserviceMode, generateId, + purposeVersionState, tenantKind, toPurposeV2, toReadModelEService, @@ -121,7 +122,15 @@ export const testCreateReversePurpose = (): ReturnType => }); const expectedPurpose: Purpose = { - versions: [], + versions: [ + { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + id: unsafeBrandId(writtenPayload.purpose!.versions[0].id), + createdAt: new Date(), + state: purposeVersionState.draft, + dailyCalls: reversePurposeSeed.dailyCalls, + }, + ], id: purpose.id, createdAt: new Date(), eserviceId: unsafeBrandId(reversePurposeSeed.eServiceId), From 229e88d1e1bc0336e9e05ecf69dcf254a87c070d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 11:20:54 +0200 Subject: [PATCH 359/537] Refactor --- .../purpose-process/src/services/purposeService.ts | 12 ++++++------ packages/purpose-process/src/services/validators.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 499e412ba2..db81353875 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -15,7 +15,6 @@ import { PurposeVersionId, ownership, purposeEventToBinaryData, - purposeVersionState, } from "pagopa-interop-models"; import { eserviceNotFound, @@ -30,7 +29,11 @@ import { } from "../model/domain/errors.js"; import { toCreateEventWaitingForApprovalPurposeVersionDeleted } from "../model/domain/toEvent.js"; import { ReadModelService } from "./readModelService.js"; -import { isRiskAnalysisFormValid, purposeIsDraft } from "./validators.js"; +import { + isRiskAnalysisFormValid, + isDeletableVersion, + purposeIsDraft, +} from "./validators.js"; const retrievePurpose = async ( purposeId: PurposeId, @@ -180,10 +183,7 @@ export function purposeServiceBuilder( const purposeVersion = retrievePurposeVersion(versionId, purpose); - if ( - purposeVersion.state !== purposeVersionState.waitingForApproval || - purpose.data.versions.length === 1 - ) { + if (!isDeletableVersion(purposeVersion, purpose.data)) { throw purposeVersionCannotBeDeleted(purposeId, versionId); } diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 4c9c595753..d773322103 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -4,6 +4,7 @@ import { } from "pagopa-interop-commons"; import { Purpose, + PurposeVersion, RiskAnalysisForm, TenantKind, purposeVersionState, @@ -29,3 +30,10 @@ export const isRiskAnalysisFormValid = ( export const purposeIsDraft = (purpose: Purpose): boolean => !purpose.versions.some((v) => v.state !== purposeVersionState.draft); + +export const isDeletableVersion = ( + purposeVersion: PurposeVersion, + purpose: Purpose +): boolean => + purposeVersion.state === purposeVersionState.waitingForApproval && + purpose.versions.length !== 1; From bc0252798548c7794cdc9d4c2c6011c6a93f1d2c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 11:32:34 +0200 Subject: [PATCH 360/537] Refactor --- packages/catalog-process/package.json | 1 - packages/catalog-process/src/services/catalogService.ts | 4 ++-- .../catalog-process/test/catalogService.integration.test.ts | 6 +++--- packages/commons/package.json | 1 + packages/commons/src/index.ts | 1 + .../src/utilities => commons/src/utils}/date.ts | 2 +- pnpm-lock.yaml | 6 +++--- 7 files changed, 11 insertions(+), 10 deletions(-) rename packages/{catalog-process/src/utilities => commons/src/utils}/date.ts (57%) diff --git a/packages/catalog-process/package.json b/packages/catalog-process/package.json index c1978af184..33f34349e4 100644 --- a/packages/catalog-process/package.json +++ b/packages/catalog-process/package.json @@ -41,7 +41,6 @@ "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", "axios": "1.4.0", - "date-fns": "3.3.1", "dotenv-flow": "3.2.0", "express": "4.18.2", "mongodb": "5.6.0", diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 5b18c2a92e..2a7cfca2ee 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -3,6 +3,7 @@ import { DB, FileManager, eventRepository, + formatDateAndTime, hasPermission, logger, riskAnalysisValidatedFormToNewRiskAnalysis, @@ -91,7 +92,6 @@ import { tenantNotFound, eServiceRiskAnalysisNotFound, } from "../model/domain/errors.js"; -import { formatClonedEServiceDate } from "../utilities/date.js"; import { ReadModelService } from "./readModelService.js"; import { assertRequesterAllowed, @@ -1158,7 +1158,7 @@ export function catalogServiceBuilder( const clonedEServiceName = `${ eservice.data.name - } - clone - ${formatClonedEServiceDate(new Date())}`; + } - clone - ${formatDateAndTime(new Date())}`; if ( await readModelService.getEServiceByNameAndProducerId({ diff --git a/packages/catalog-process/test/catalogService.integration.test.ts b/packages/catalog-process/test/catalogService.integration.test.ts index 23d01ae8c1..262e6313a7 100644 --- a/packages/catalog-process/test/catalogService.integration.test.ts +++ b/packages/catalog-process/test/catalogService.integration.test.ts @@ -23,6 +23,7 @@ import { ReadModelRepository, TenantCollection, fileManagerDeleteError, + formatDateAndTime, initDB, initFileManager, unexpectedFieldError, @@ -117,7 +118,6 @@ import { tenantKindNotFound, tenantNotFound, } from "../src/model/domain/errors.js"; -import { formatClonedEServiceDate } from "../src/utilities/date.js"; import { addOneAgreement, addOneEService, @@ -2814,7 +2814,7 @@ describe("database test", async () => { const expectedEService: EService = { ...eservice, id: unsafeBrandId(writtenPayload.eservice!.id), - name: `${eservice.name} - clone - ${formatClonedEServiceDate( + name: `${eservice.name} - clone - ${formatDateAndTime( cloneTimestamp )}`, descriptors: [expectedDescriptor], @@ -2892,7 +2892,7 @@ describe("database test", async () => { const cloneTimestamp = new Date(); const conflictEServiceName = `${ eservice1.name - } - clone - ${formatClonedEServiceDate(cloneTimestamp)}`; + } - clone - ${formatDateAndTime(cloneTimestamp)}`; const eservice2: EService = { ...mockEService, diff --git a/packages/commons/package.json b/packages/commons/package.json index 70229da87f..c5ba638f30 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -25,6 +25,7 @@ "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", "connection-string": "4.3.6", + "date-fns": "3.3.1", "express-winston": "4.2.0", "jsonwebtoken": "9.0.2", "jwks-rsa": "3.0.1", diff --git a/packages/commons/src/index.ts b/packages/commons/src/index.ts index 8834f68333..a410a28594 100644 --- a/packages/commons/src/index.ts +++ b/packages/commons/src/index.ts @@ -11,3 +11,4 @@ export * from "./repositories/db.js"; export * from "./types/index.js"; export * from "./auth/token/index.js"; export * from "./risk-analysis/index.js"; +export * from "./utils/date.js"; diff --git a/packages/catalog-process/src/utilities/date.ts b/packages/commons/src/utils/date.ts similarity index 57% rename from packages/catalog-process/src/utilities/date.ts rename to packages/commons/src/utils/date.ts index eac94bf33f..e674951f82 100644 --- a/packages/catalog-process/src/utilities/date.ts +++ b/packages/commons/src/utils/date.ts @@ -1,5 +1,5 @@ import { format } from "date-fns"; -export function formatClonedEServiceDate(date: Date): string { +export function formatDateAndTime(date: Date): string { return format(date, "dd/MM/yyyy HH:mm:ss"); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ffb46e54d..d284e1365b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -385,9 +385,6 @@ importers: axios: specifier: 1.4.0 version: 1.4.0 - date-fns: - specifier: 3.3.1 - version: 3.3.1 dotenv-flow: specifier: 3.2.0 version: 3.2.0 @@ -552,6 +549,9 @@ importers: connection-string: specifier: 4.3.6 version: 4.3.6 + date-fns: + specifier: 3.3.1 + version: 3.3.1 express-winston: specifier: 4.2.0 version: 4.2.0(winston@3.9.0) From 43591e77908fbd39ea6724fcd17fda51251ea03d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 11:37:08 +0200 Subject: [PATCH 361/537] Update naming logic --- .../src/services/purposeService.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 2bb91ccc47..82d50c6b2c 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -2,6 +2,7 @@ import { CreateEvent, DB, eventRepository, + formatDateAndTime, logger, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, @@ -779,10 +780,25 @@ export function purposeServiceBuilder( } : undefined; + const currentDate = new Date(); + const clonedPurposeName = `${ + purposeToClone.data.title + } - clone - ${formatDateAndTime(currentDate)}`; + + const purposeWithSameName = await readModelService.getSpecificPurpose( + unsafeBrandId(seed.eserviceId), + organizationId, + clonedPurposeName + ); + + if (purposeWithSameName) { + throw duplicatedPurposeName(clonedPurposeName); + } + const clonedPurpose: Purpose = { - title: purposeToClone.data.title, + title: clonedPurposeName, id: generateId(), - createdAt: new Date(), + createdAt: currentDate, eserviceId: unsafeBrandId(seed.eserviceId), consumerId: organizationId, description: purposeToClone.data.description, From 9383c20ce1283bba9a8f7b6a4e06ad1b056a8bb2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 12:07:52 +0200 Subject: [PATCH 362/537] Minor improvement --- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 82d50c6b2c..556de7b55f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -742,6 +742,8 @@ export function purposeServiceBuilder( logger.info(`Cloning Purpose ${purposeId}`); const tenant = await retrieveTenant(organizationId, readModelService); + assertTenantKindExists(tenant); + const purposeToClone = await retrievePurpose(purposeId, readModelService); if (purposeIsDraft(purposeToClone.data)) { @@ -808,8 +810,6 @@ export function purposeServiceBuilder( riskAnalysisForm: clonedRiskAnalysisForm, }; - assertTenantKindExists(tenant); - const isRiskAnalysisValid = clonedRiskAnalysisForm ? validateRiskAnalysis( riskAnalysisFormToRiskAnalysisFormToValidate( From f240567e765753556f15216b1822906a0855ecdc Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 12:26:27 +0200 Subject: [PATCH 363/537] updated tests --- packages/purpose-process/test/testUpdatePurpose.ts | 13 ++++++++++--- packages/purpose-process/test/utils.ts | 12 ++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts index f931c78897..91e63d19be 100644 --- a/packages/purpose-process/test/testUpdatePurpose.ts +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { unexpectedRulesVersionError } from "pagopa-interop-commons"; import { @@ -28,7 +29,7 @@ import { RiskAnalysis, eserviceMode, } from "pagopa-interop-models"; -import { describe, it, expect } from "vitest"; +import { describe, it, expect, vi } from "vitest"; import { purposeNotFound, organizationIsNotTheConsumer, @@ -115,6 +116,9 @@ export const testUpdatePurpose = (): ReturnType => }; it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + await addOnePurpose(purposeForDeliver, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(tenant, tenants); @@ -148,12 +152,15 @@ export const testUpdatePurpose = (): ReturnType => purposeForDeliver, purposeUpdateContent, validRiskAnalysis, - writtenPayload + writtenPayload.purpose!.riskAnalysisForm! ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + await addOnePurpose(purposeForReceive, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); await writeInReadmodel(tenant, tenants); @@ -187,7 +194,7 @@ export const testUpdatePurpose = (): ReturnType => purposeForReceive, reversePurposeUpdateContent, validRiskAnalysis, - writtenPayload + writtenPayload.purpose!.riskAnalysisForm! ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 09c30cd5b5..fb9d1a9918 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -9,7 +9,6 @@ import { writeInReadmodel, } from "pagopa-interop-commons-test"; import { - DraftPurposeUpdatedV2, EService, Purpose, PurposeEvent, @@ -25,6 +24,7 @@ import { ApiReversePurposeUpdateContent, ApiRiskAnalysisFormSeed, } from "../src/model/domain/models.js"; +import { PurposeRiskAnalysisFormV2 } from "../../models/dist/gen/v2/purpose/riskAnalysis.js"; export const addOnePurpose = async ( purpose: Purpose, @@ -78,22 +78,22 @@ export const createUpdatedPurpose = ( | ApiPurposeUpdateContent | ApiReversePurposeUpdateContent, mockValidRiskAnalysis: RiskAnalysis, - writtenPayload: DraftPurposeUpdatedV2 + writtenRiskAnalysisForm: PurposeRiskAnalysisFormV2 ): Purpose => ({ ...mockPurpose, title: purposeUpdateContent.title, description: purposeUpdateContent.description, isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, freeOfChargeReason: purposeUpdateContent.freeOfChargeReason, - updatedAt: new Date(Number(writtenPayload.purpose?.updatedAt)), + updatedAt: new Date(), riskAnalysisForm: { ...mockValidRiskAnalysis.riskAnalysisForm, - id: unsafeBrandId(writtenPayload.purpose!.riskAnalysisForm!.id), + id: unsafeBrandId(writtenRiskAnalysisForm.id), singleAnswers: mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( (singleAnswer) => ({ ...singleAnswer, id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.singleAnswers.find( + writtenRiskAnalysisForm.singleAnswers.find( (sa) => sa.key === singleAnswer.key )!.id ), @@ -103,7 +103,7 @@ export const createUpdatedPurpose = ( (multiAnswer) => ({ ...multiAnswer, id: unsafeBrandId( - writtenPayload.purpose!.riskAnalysisForm!.multiAnswers.find( + writtenRiskAnalysisForm.multiAnswers.find( (ma) => ma.key === multiAnswer.key )!.id ), From ca0cb574b3cdf77314fb70e3ef0919e09c9d4f2b Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 13:00:39 +0200 Subject: [PATCH 364/537] added missing dailyCalls --- packages/purpose-process/src/services/purposeService.ts | 7 +++++++ packages/purpose-process/test/utils.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a9c1f3cd38..f97a3987b8 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -460,6 +460,13 @@ const updatePurposeInternal = async ( const updatedPurpose: Purpose = { ...purpose.data, ...updateContent, + versions: [ + { + ...purpose.data.versions[0], + dailyCalls: updateContent.dailyCalls, + updatedAt: new Date(), + }, + ], updatedAt: new Date(), riskAnalysisForm: newRiskAnalysis, }; diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index fb9d1a9918..814cf722cf 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -85,6 +85,13 @@ export const createUpdatedPurpose = ( description: purposeUpdateContent.description, isFreeOfCharge: purposeUpdateContent.isFreeOfCharge, freeOfChargeReason: purposeUpdateContent.freeOfChargeReason, + versions: [ + { + ...mockPurpose.versions[0], + dailyCalls: purposeUpdateContent.dailyCalls, + updatedAt: new Date(), + }, + ], updatedAt: new Date(), riskAnalysisForm: { ...mockValidRiskAnalysis.riskAnalysisForm, From 1b51f5c23a77a58cd552359dc955dce5a9ca10c2 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 13:00:56 +0200 Subject: [PATCH 365/537] added missing useRealTimers --- packages/purpose-process/test/testUpdatePurpose.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts index 91e63d19be..aa7fcfeb2a 100644 --- a/packages/purpose-process/test/testUpdatePurpose.ts +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -156,6 +156,7 @@ export const testUpdatePurpose = (): ReturnType => ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + vi.useRealTimers(); }); it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { vi.useFakeTimers(); @@ -198,6 +199,7 @@ export const testUpdatePurpose = (): ReturnType => ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + vi.useRealTimers(); }); it("Should throw purposeNotFound if the purpose doesn't exist", async () => { await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); From 1825ec1388abc75832a337bf73f73e5bd5d28040 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 13:01:28 +0200 Subject: [PATCH 366/537] improved check isDraft --- packages/purpose-process/src/services/validators.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 9e2beca574..8309cf5b09 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -45,7 +45,7 @@ export const isRiskAnalysisFormValid = ( }; export const purposeIsDraft = (purpose: Purpose): boolean => - !purpose.versions.some((v) => v.state !== purposeVersionState.draft); + purpose.versions[0]?.state === purposeVersionState.draft; export const isRejectable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.waitingForApproval; From e4a20a56ac1b5cf2d262f24074f8b2731e093fe8 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 13:39:18 +0200 Subject: [PATCH 367/537] improved purposeIsDraft --- packages/purpose-process/src/services/validators.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 8309cf5b09..00f189b011 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -45,7 +45,8 @@ export const isRiskAnalysisFormValid = ( }; export const purposeIsDraft = (purpose: Purpose): boolean => - purpose.versions[0]?.state === purposeVersionState.draft; + purpose.versions.length === 1 && + purpose.versions[0].state === purposeVersionState.draft; export const isRejectable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.waitingForApproval; From cec5690d5c54f6fd16ce5c29eb4f977122995154 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 30 Apr 2024 15:09:29 +0200 Subject: [PATCH 368/537] Revert "improved check isDraft" This reverts commit 1825ec1388abc75832a337bf73f73e5bd5d28040. --- packages/purpose-process/src/services/validators.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 00f189b011..9e2beca574 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -45,8 +45,7 @@ export const isRiskAnalysisFormValid = ( }; export const purposeIsDraft = (purpose: Purpose): boolean => - purpose.versions.length === 1 && - purpose.versions[0].state === purposeVersionState.draft; + !purpose.versions.some((v) => v.state !== purposeVersionState.draft); export const isRejectable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.waitingForApproval; From 0f23f2bb8819599c09882f234824f55af53e0627 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 15:29:13 +0200 Subject: [PATCH 369/537] Add tests --- .../test/purposeService.integration.test.ts | 2 + .../purpose-process/test/testClonePurpose.ts | 304 ++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 packages/purpose-process/test/testClonePurpose.ts diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 061431fb99..6d72e030fa 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -40,6 +40,7 @@ import { testSuspendPurposeVersion } from "./testSuspendPurposeVersion.js"; import { testGetPurposes } from "./testGetPurposes.js"; import { testCreatePurpose } from "./testCreatePurpose.js"; import { testCreateReversePurpose } from "./testCreateReversePurpose.js"; +import { testClonePurpose } from "./testClonePurpose.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -107,5 +108,6 @@ describe("Integration tests", async () => { testGetPurposes(); testCreatePurpose(); testCreateReversePurpose(); + testClonePurpose(); }); }); diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts new file mode 100644 index 0000000000..8aa664667c --- /dev/null +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -0,0 +1,304 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + decodeProtobufPayload, + getMockAgreement, + getMockEService, + getMockPurpose, + getMockPurposeVersion, + getMockTenant, + readLastEventByStreamId, + writeInReadmodel, +} from "pagopa-interop-commons-test/index.js"; +import { + Purpose, + PurposeClonedV2, + agreementState, + generateId, + purposeVersionState, + tenantKind, + toPurposeV2, + unsafeBrandId, +} from "pagopa-interop-models"; +import { describe, expect, it, vi } from "vitest"; +import { formatDateAndTime } from "pagopa-interop-commons"; +import { + duplicatedPurposeName, + purposeCannotBeCloned, + purposeNotFound, + tenantKindNotFound, +} from "../src/model/domain/errors.js"; +import { addOnePurpose } from "./utils.js"; +import { + agreements, + postgresDB, + purposeService, + purposes, + tenants, +} from "./purposeService.integration.test.js"; + +export const testClonePurpose = (): ReturnType => + describe("clonePurpose", async () => { + it("should write on event-store for the cloning of a purpose", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(mockAgreement, agreements); + + const { purpose } = await purposeService.clonePurpose({ + purposeId: mockPurpose.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + purpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: purpose.id, + version: "0", + type: "PurposeCloned", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeClonedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + id: unsafeBrandId(writtenPayload.purpose!.id), + title: `${mockPurpose.title} - clone - ${formatDateAndTime( + new Date() + )}`, + versions: [ + { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + id: unsafeBrandId(writtenPayload.purpose!.versions[0].id), + state: purposeVersionState.draft, + createdAt: new Date(), + dailyCalls: mockPurpose.versions[0].dailyCalls, + }, + ], + createdAt: new Date(), + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + + vi.useRealTimers(); + }); + it("should throw purposeNotFound if the purpose to clone doesn't exist", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.clonePurpose({ + purposeId: mockPurpose.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeNotFound(mockPurpose.id)); + }); + it("should throw purposeCannotBeCloned if the purpose is in draft (no versions)", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.clonePurpose({ + purposeId: mockPurpose.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeCloned(mockPurpose.id)); + }); + it("should throw purposeCannotBeCloned if the purpose is in draft (draft version)", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [getMockPurposeVersion(purposeVersionState.draft)], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.clonePurpose({ + purposeId: mockPurpose.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + }) + ).rejects.toThrowError(purposeCannotBeCloned(mockPurpose.id)); + }); + it("should throw duplicatedPurposeName if a purpose with the same name already exists", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurposeToClone: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const mockPurposeWithSameName: Purpose = { + ...getMockPurpose(), + title: `${mockPurposeToClone.title} - clone - ${formatDateAndTime( + new Date() + )}`, + eserviceId: mockEService.id, + consumerId: mockTenant.id, + }; + + await addOnePurpose(mockPurposeToClone, postgresDB, purposes); + await addOnePurpose(mockPurposeWithSameName, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.clonePurpose({ + purposeId: mockPurposeToClone.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + }) + ).rejects.toThrowError( + duplicatedPurposeName(mockPurposeWithSameName.title) + ); + }); + it("should throw tenantKindNotFound if the tenant kind doesn't exist", async () => { + const mockTenant = { + ...getMockTenant(), + kind: undefined, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.clonePurpose({ + purposeId: mockPurpose.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + }) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + }); From 5ba8af6cee3d03fd1a40619a728c339fd2f3653a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 16:17:19 +0200 Subject: [PATCH 370/537] Replace query --- .../src/services/readModelService.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 8905d0b690..257bef2421 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -171,6 +171,21 @@ export function readModelServiceBuilder( } : {}; + const eserviceIds = + producersIds.length > 0 + ? await eservices + .find({ "data.producerId": { $in: producersIds } }) + .toArray() + .then((results) => + results.map((eservice) => eservice.data.id.toString()) + ) + : []; + + const producerIdsFilter: ReadModelFilter = + ReadModelRepository.arrayToFilter(eserviceIds, { + "data.eserviceId": { $in: eserviceIds }, + }); + const aggregationPipeline = [ { $match: { @@ -179,26 +194,9 @@ export function readModelServiceBuilder( ...consumersIdsFilter, ...versionStateFilter, ...draftFilter, + ...producerIdsFilter, } satisfies ReadModelFilter, }, - ...(producersIds.length > 0 - ? [ - { - $lookup: { - from: "eservices", - localField: "data.eserviceId", - foreignField: "data.id", - as: "eservices", - }, - }, - { $unwind: "$eservices" }, - { - $match: { - "eservices.data.producerId": { $in: producersIds }, - }, - }, - ] - : []), { $project: { data: 1, From 76ee7af2f672a14511d6e3b6fafa93f50f9f251b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 30 Apr 2024 17:49:51 +0200 Subject: [PATCH 371/537] Fix errors numbering --- packages/purpose-process/src/model/domain/errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 904225d084..6588f9ac50 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -30,8 +30,8 @@ export const errorCodes = { riskAnalysisValidationFailed: "0015", purposeNotInDraftState: "0016", purposeCannotBeDeleted: "0017", - duplicatedPurposeName: "0018", agreementNotFound: "0018", + duplicatedPurposeName: "0019", eserviceRiskAnalysisNotFound: "0020", }; From 29d6870fdfebaabaca700cce9be2f5e9e15f9c63 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Tue, 30 Apr 2024 18:51:54 +0200 Subject: [PATCH 372/537] fix as suggest --- packages/purpose-process/src/services/purposeService.ts | 4 ++-- packages/purpose-process/test/testCreatePurpose.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a945b6808b..9da37dc54b 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -142,7 +142,7 @@ const retrieveTenant = async ( return tenant; }; -const retrieveAgreement = async ( +const retrieveActiveAgreement = async ( eserviceId: EServiceId, consumerId: TenantId, readModelService: ReadModelService @@ -604,7 +604,7 @@ export function purposeServiceBuilder( tenant.kind ); - await retrieveAgreement(eserviceId, consumerId, readModelService); + await retrieveActiveAgreement(eserviceId, consumerId, readModelService); const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts index 305f560c79..a8fd7aa6d1 100644 --- a/packages/purpose-process/test/testCreatePurpose.ts +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -182,7 +182,7 @@ export const testCreatePurpose = (): ReturnType => ) ).rejects.toThrowError(missingFreeOfChargeReason()); }); - it("should throw tenantKindNotFound", async () => { + it("should throw tenantKindNotFound if the kind doesn't exists", async () => { const tenantWithoutKind: Tenant = { ...tenant, kind: undefined, @@ -216,7 +216,7 @@ export const testCreatePurpose = (): ReturnType => ) ).rejects.toThrowError(tenantKindNotFound(tenantWithoutKind.id)); }); - it("should throw tenantNotFound", async () => { + it("should throw tenantNotFound if the tenant doesn't exists", async () => { expect( purposeService.createPurpose( purposeSeed, @@ -225,7 +225,7 @@ export const testCreatePurpose = (): ReturnType => ) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); - it("should throw agreementNotFound", async () => { + it("should throw agreementNotFound if the agreement doesn't exists ", async () => { const descriptor: Descriptor = { ...descriptor1, id: generateId(), @@ -315,7 +315,7 @@ export const testCreatePurpose = (): ReturnType => ]) ); }); - it("should throw duplicatedPurposeName if a purpose with same name alreay exist", async () => { + it("should throw duplicatedPurposeName if a purpose with same name alreay exists", async () => { const existingPurpose: Purpose = { ...getMockPurpose(), eserviceId: unsafeBrandId(purposeSeed.eserviceId), From 646eea8e196eb3891661e4b57ef9fdf00aaeee7e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 11:30:54 +0200 Subject: [PATCH 373/537] Renaming --- .../open-api/purpose-service-spec.yml | 2 +- .../purpose-process/src/routers/PurposeRouter.ts | 6 +++--- .../src/services/purposeService.ts | 2 +- .../src/utilities/errorMappers.ts | 2 +- .../test/testCreateReversePurpose.ts | 16 ++++++++-------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index ebfdd8cc7f..516a666c74 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -156,7 +156,7 @@ paths: - $ref: "#/components/parameters/CorrelationIdHeader" tags: - purpose - operationId: createPurposeFromEService + operationId: createReversePurpose description: Create a Purpose for EService with Receive mode requestBody: content: diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 1d82541563..c1568de016 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -23,7 +23,7 @@ import { makeApiProblem } from "../model/domain/errors.js"; import { archivePurposeVersionErrorMapper, createPurposeErrorMapper, - createPurposeFromEServiceErrorMapper, + createReversePurposeErrorMapper, deletePurposeErrorMapper, deletePurposeVersionErrorMapper, getPurposeErrorMapper, @@ -139,7 +139,7 @@ const purposeRouter = ( async (req, res) => { try { const { purpose, isRiskAnalysisValid } = - await purposeService.createPurposeFromEService( + await purposeService.createReversePurpose( req.ctx.authData.organizationId, req.body, req.ctx.correlationId @@ -151,7 +151,7 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - createPurposeFromEServiceErrorMapper + createReversePurposeErrorMapper ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index e4b28ad029..39da2b0694 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -661,7 +661,7 @@ export function purposeServiceBuilder( ); return { purpose, isRiskAnalysisValid: validatedFormSeed !== undefined }; }, - async createPurposeFromEService( + async createReversePurpose( organizationId: TenantId, seed: ApiReversePurposeSeed, correlationId: string diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 64be40e1a2..a43c5e763e 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -105,7 +105,7 @@ export const createPurposeErrorMapper = (error: ApiError): number => .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); -export const createPurposeFromEServiceErrorMapper = ( +export const createReversePurposeErrorMapper = ( error: ApiError ): number => match(error.code) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index 870b4e386d..cfa78f5e0d 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -97,7 +97,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(consumer, tenants); await writeInReadmodel(mockAgreement, agreements); - const { purpose } = await purposeService.createPurposeFromEService( + const { purpose } = await purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() @@ -190,7 +190,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(mockAgreement, agreements); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( producer.id, reversePurposeSeed, generateId() @@ -241,7 +241,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(mockAgreement, agreements); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() @@ -294,7 +294,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(mockAgreement, agreements); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() @@ -347,7 +347,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(mockAgreement, agreements); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() @@ -398,7 +398,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(mockAgreement, agreements); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() @@ -441,7 +441,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(consumer, tenants); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() @@ -500,7 +500,7 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(mockAgreement, agreements); expect( - purposeService.createPurposeFromEService( + purposeService.createReversePurpose( consumer.id, reversePurposeSeed, generateId() From 1dba2c0f5bbb78d731125c5757d72dd0ef1a3400 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 11:35:08 +0200 Subject: [PATCH 374/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index dc0bf3e179..e18b78945e 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -693,7 +693,7 @@ export function purposeServiceBuilder( assertTenantKindExists(producer); - await retrieveAgreement(eserviceId, consumerId, readModelService); + await retrieveActiveAgreement(eserviceId, consumerId, readModelService); const purposeWithSameName = await readModelService.getSpecificPurpose( eserviceId, From 8a7b9974aa42c0be542eba3e54aa49f3399c2883 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 11:36:09 +0200 Subject: [PATCH 375/537] Renaming --- packages/purpose-process/open-api/purpose-service-spec.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index 516a666c74..8fd75f6ae9 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -162,7 +162,7 @@ paths: content: application/json: schema: - $ref: "#/components/schemas/EServicePurposeSeed" + $ref: "#/components/schemas/ReversePurposeSeed" required: true responses: "200": @@ -1206,7 +1206,7 @@ components: format: uuid required: - eserviceId - EServicePurposeSeed: + ReversePurposeSeed: type: object description: contains the expected payload for purpose creation. properties: From 414b737f026f8d2091703a4eacab9015234f2ead Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 11:39:00 +0200 Subject: [PATCH 376/537] Fix after renaming --- packages/purpose-process/src/model/domain/models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 0bd9f1310f..cae29e1293 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -40,5 +40,5 @@ export type ApiGetPurposesFilters = { }; export type ApiReversePurposeSeed = z.infer< - typeof api.schemas.EServicePurposeSeed + typeof api.schemas.ReversePurposeSeed >; From 1f69870d27af9755de3e6f3c2be8bc1b76ca9cf2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 11:40:25 +0200 Subject: [PATCH 377/537] Delete empty line --- packages/purpose-process/test/purposeService.integration.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 061431fb99..ffdf21bb56 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -68,7 +68,6 @@ describe("Integration tests", async () => { eservices = readModelRepository.eservices; tenants = readModelRepository.tenants; agreements = readModelRepository.agreements; - readModelService = readModelServiceBuilder(readModelRepository); postgresDB = initDB({ username: config.eventStoreDbUsername, From 5517ec15b6666dc31e38505dc1be583f241e667f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 12:05:55 +0200 Subject: [PATCH 378/537] Update version and todo --- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b0253b7294..675ce40027 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -785,8 +785,8 @@ export function purposeServiceBuilder( riskAnalysisFormToClone ? { id: generateId(), - version: "1", // TO DO: "0"? - riskAnalysisId: riskAnalysisFormToClone.riskAnalysisId, // TO DO double-check + version: riskAnalysisFormToClone.version, // TO DO double-check + riskAnalysisId: riskAnalysisFormToClone.riskAnalysisId, singleAnswers: riskAnalysisFormToClone.singleAnswers.map( (answer) => ({ ...answer, From 754fac349e2d15c5e3ca36a80bab0a07fb2d10df Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 2 May 2024 12:18:50 +0200 Subject: [PATCH 379/537] added check for duplicated title --- .../src/model/domain/errors.ts | 9 +++++++ .../src/services/purposeService.ts | 11 +++++++++ .../src/services/readModelService.ts | 15 ++++++++++++ .../src/utilities/errorMappers.ts | 23 +++++++++++------- .../purpose-process/test/testUpdatePurpose.ts | 24 +++++++++++++++++++ 5 files changed, 74 insertions(+), 8 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index b708198b7e..fa7208f2bb 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -28,6 +28,7 @@ export const errorCodes = { missingFreeOfChargeReason: "0014", riskAnalysisValidationFailed: "0015", purposeNotInDraftState: "0016", + duplicatedPurposeTitle: "0017", }; export type ErrorCodes = keyof typeof errorCodes; @@ -186,3 +187,11 @@ export function missingRejectionReason(): ApiError { title: "Rejection reason can't be omitted", }); } + +export function duplicatedPurposeTitle(title: string): ApiError { + return new ApiError({ + detail: `Purpose with title: ${title} already exists`, + code: "duplicatedPurposeTitle", + title: "Duplicated Purpose Title", + }); +} diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index f97a3987b8..9b7e01d3b5 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -27,6 +27,7 @@ import { eserviceMode, } from "pagopa-interop-models"; import { + duplicatedPurposeTitle, eserviceNotFound, missingRejectionReason, notValidVersionState, @@ -428,6 +429,16 @@ const updatePurposeInternal = async ( assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); assertPurposeIsDraft(purpose.data); + const purposeWithSameTitle = await readModelService.getSpecificPurpose( + purpose.data.eserviceId, + purpose.data.consumerId, + updateContent.title + ); + + if (purposeWithSameTitle) { + throw duplicatedPurposeTitle(updateContent.title); + } + const eservice = await retrieveEService( purpose.data.eserviceId, readModelService diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index f0f307a85c..9d6bc95ae7 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -4,6 +4,7 @@ import { EServiceCollection, TenantCollection, PurposeCollection, + ReadModelFilter, } from "pagopa-interop-commons"; import { EService, @@ -111,6 +112,20 @@ export function readModelServiceBuilder( ): Promise | undefined> { return getPurpose(purposes, { "data.id": id }); }, + async getSpecificPurpose( + eserviceId: EServiceId, + consumerId: TenantId, + title: string + ): Promise | undefined> { + return getPurpose(purposes, { + "data.eserviceId": eserviceId, + "data.consumerId": consumerId, + "data.title": { + $regex: `^${ReadModelRepository.escapeRegExp(title)}$$`, + $options: "i", + }, + } satisfies ReadModelFilter); + }, }; } diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index b24cdefea5..5e9e12816a 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -69,13 +69,20 @@ export const rejectPurposeVersionErrorMapper = ( export const updatePurposeErrorMapper = (error: ApiError): number => match(error.code) - .with("eServiceModeNotAllowed", () => HTTP_STATUS_BAD_REQUEST) - .with("missingFreeOfChargeReason", () => HTTP_STATUS_BAD_REQUEST) - .with("tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with( + "eServiceModeNotAllowed", + "missingFreeOfChargeReason", + "tenantKindNotFound", + "riskAnalysisValidationFailed", + "eserviceNotFound", + "tenantNotFound", + () => HTTP_STATUS_BAD_REQUEST + ) + .with( + "organizationIsNotTheConsumer", + "purposeNotInDraftState", + () => HTTP_STATUS_FORBIDDEN + ) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) - .with("purposeNotInDraftState", () => HTTP_STATUS_FORBIDDEN) - .with("eserviceNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts index aa7fcfeb2a..0cb9cab8ab 100644 --- a/packages/purpose-process/test/testUpdatePurpose.ts +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -40,6 +40,7 @@ import { tenantNotFound, tenantKindNotFound, riskAnalysisValidationFailed, + duplicatedPurposeTitle, } from "../src/model/domain/errors.js"; import { ApiPurposeUpdateContent, @@ -263,6 +264,29 @@ export const testUpdatePurpose = (): ReturnType => ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); } ); + it("Should throw duplicatedPurposeTitle if the purpose title already exists", async () => { + const purposeWithDuplicatedTitle = { + ...purposeForDeliver, + id: unsafeBrandId(generateId()), + title: "duplicated", + }; + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await addOnePurpose(purposeWithDuplicatedTitle, postgresDB, purposes); + + expect( + purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: { + ...purposeUpdateContent, + title: purposeWithDuplicatedTitle.title, + }, + organizationId: tenant.id, + correlationId: generateId(), + }) + ).rejects.toThrowError( + duplicatedPurposeTitle(purposeWithDuplicatedTitle.title) + ); + }); it("Should throw eserviceNotFound if the eservice doesn't exist", async () => { const eserviceId: EServiceId = unsafeBrandId(generateId()); const mockPurpose: Purpose = { From 836ccc5d2532ba784b5788cdd5c6af9781018e9c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 12:27:32 +0200 Subject: [PATCH 380/537] Refactor --- .../src/services/purposeService.ts | 7 +++++-- .../purpose-process/src/services/validators.ts | 18 ++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a854351844..4e09e2e5d3 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -34,6 +34,7 @@ import { organizationIsNotTheConsumer, organizationIsNotTheProducer, organizationNotAllowed, + purposeCannotBeDeleted, purposeNotFound, purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, @@ -63,7 +64,7 @@ import { validateAndTransformRiskAnalysis, assertPurposeIsDraft, isRejectable, - assertPurposeIsDeletable, + isDeletable, } from "./validators.js"; const retrievePurpose = async ( @@ -344,7 +345,9 @@ export function purposeServiceBuilder( assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); - assertPurposeIsDeletable(purpose.data); + if (!isDeletable(purpose.data)) { + throw purposeCannotBeDeleted(purpose.data.id); + } const event = purposeIsDraft(purpose.data) ? toCreateEventDraftPurposeDeleted({ diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 009bcf65ce..5fa8d7132a 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -20,7 +20,6 @@ import { eServiceModeNotAllowed, missingFreeOfChargeReason, organizationIsNotTheConsumer, - purposeCannotBeDeleted, purposeNotInDraftState, riskAnalysisValidationFailed, tenantKindNotFound, @@ -144,14 +143,9 @@ export function assertPurposeIsDraft(purpose: Purpose): void { } } -export function assertPurposeIsDeletable(purpose: Purpose): void { - if ( - purpose.versions.some( - (v) => - v.state !== purposeVersionState.draft && - v.state !== purposeVersionState.waitingForApproval - ) - ) { - throw purposeCannotBeDeleted(purpose.id); - } -} +export const isDeletable = (purpose: Purpose): boolean => + purpose.versions.every( + (v) => + v.state === purposeVersionState.draft || + v.state === purposeVersionState.waitingForApproval + ); From c2c31e253fb3e30da53f414a0ac877a4b5fd0bed Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 12:32:21 +0200 Subject: [PATCH 381/537] Fix lint --- packages/purpose-process/src/model/domain/errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index b6182da1a2..07bfe15109 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -195,7 +195,7 @@ export function duplicatedPurposeTitle(title: string): ApiError { detail: `Purpose with title: ${title} already exists`, code: "duplicatedPurposeTitle", title: "Duplicated Purpose Title", - }); + }); } export function purposeCannotBeDeleted( From 7d496447ef99128d0104c34f2e54e9a4bdccaa3b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 12:33:59 +0200 Subject: [PATCH 382/537] Fix import --- packages/purpose-process/src/services/purposeService.ts | 1 - packages/purpose-process/src/services/validators.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 3671537ebb..4724a8dc69 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -66,7 +66,6 @@ import { assertPurposeIsDraft, isRejectable, isDeletable, - assertPurposeIsDeletable, isArchivable, } from "./validators.js"; diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index fb73a7e70f..0a92e7e57f 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -4,7 +4,6 @@ import { Purpose, PurposeVersion, PurposeRiskAnalysisForm, - PurposeVersion, RiskAnalysisForm, Tenant, TenantId, @@ -154,4 +153,3 @@ export const isDeletable = (purpose: Purpose): boolean => export const isArchivable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.active || purposeVersion.state === purposeVersionState.suspended; - From cbd138cdae8b5620aa37cde2a533d06e4129bdd7 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Thu, 2 May 2024 12:51:52 +0200 Subject: [PATCH 383/537] renaming --- packages/purpose-process/src/utilities/errorMappers.ts | 2 +- packages/purpose-process/test/testCreatePurpose.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 81137036d4..67f35204ab 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -128,5 +128,5 @@ export const createPurposeErrorMapper = (error: ApiError): number => .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) - .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) + .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts index a8fd7aa6d1..9ad067670b 100644 --- a/packages/purpose-process/test/testCreatePurpose.ts +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -36,8 +36,8 @@ import { tenantNotFound, organizationIsNotTheConsumer, riskAnalysisValidationFailed, - duplicatedPurposeName, agreementNotFound, + duplicatedPurposeTitle, } from "../src/model/domain/errors.js"; import { ApiPurposeSeed } from "../src/model/domain/models.js"; import { @@ -334,6 +334,6 @@ export const testCreatePurpose = (): ReturnType => unsafeBrandId(purposeSeed.consumerId), generateId() ) - ).rejects.toThrowError(duplicatedPurposeName(purposeSeed.title)); + ).rejects.toThrowError(duplicatedPurposeTitle(purposeSeed.title)); }); }); From 8683bf7c2ea20908944dad5b38d5be7d6b1d8081 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 13:02:07 +0200 Subject: [PATCH 384/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 3 +-- packages/purpose-process/src/utilities/errorMappers.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index e09a780efa..a78e44dde7 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -31,7 +31,6 @@ import { unsafeBrandId, generateId, Agreement, - eserviceMode, RiskAnalysisId, RiskAnalysis, } from "pagopa-interop-models"; @@ -690,7 +689,7 @@ export function purposeServiceBuilder( ); if (purposeWithSameName) { - throw duplicatedPurposeName(seed.title); + throw duplicatedPurposeTitle(seed.title); } const validationResult = validateRiskAnalysis( diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 24f1ebebb3..6c5b2476fd 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -149,6 +149,6 @@ export const createReversePurposeErrorMapper = ( "riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST ) - .with("duplicatedPurposeName", () => HTTP_STATUS_CONFLICT) + .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From a00d8fb4531bca2eadb1f09bb2705aab3155d13d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 13:02:15 +0200 Subject: [PATCH 385/537] Add test for name check --- .../test/testCreateReversePurpose.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index cfa78f5e0d..cec09058a8 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -5,6 +5,7 @@ import { getMockAgreement, getMockDescriptor, getMockDocument, + getMockPurpose, getMockTenant, getMockValidRiskAnalysis, readLastEventByStreamId, @@ -33,6 +34,7 @@ import { unexpectedRulesVersionError } from "pagopa-interop-commons"; import { ApiReversePurposeSeed } from "../src/model/domain/models.js"; import { agreementNotFound, + duplicatedPurposeTitle, eServiceModeNotAllowed, eserviceRiskAnalysisNotFound, missingFreeOfChargeReason, @@ -46,6 +48,7 @@ import { eservices, postgresDB, purposeService, + purposes, tenants, } from "./purposeService.integration.test.js"; @@ -448,6 +451,66 @@ export const testCreateReversePurpose = (): ReturnType => ) ).rejects.toThrowError(agreementNotFound(mockEService.id, consumer.id)); }); + it("should throw duplicatePurposeTitle if a purpose with the same name already exists", async () => { + const consumer = getMockTenant(); + const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; + + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + publishedAt: new Date(), + interface: getMockDocument(), + }; + + const mockRiskAnalysis = getMockValidRiskAnalysis(tenantKind.PA); + const mockEService: EService = { + ...getMockEService(), + producerId: producer.id, + riskAnalysis: [mockRiskAnalysis], + descriptors: [mockDescriptor], + mode: eserviceMode.receive, + }; + + const mockAgreement: Agreement = { + ...getMockAgreement(), + eserviceId: mockEService.id, + consumerId: consumer.id, + state: agreementState.active, + }; + + const purposeTitle = "test purpose title"; + const mockPurpose: Purpose = { + ...getMockPurpose(), + title: purposeTitle, + eserviceId: mockEService.id, + consumerId: consumer.id, + }; + + const reversePurposeSeed: ApiReversePurposeSeed = { + eServiceId: mockEService.id, + consumerId: consumer.id, + riskAnalysisId: mockRiskAnalysis.id, + title: purposeTitle, + description: "test purpose description", + isFreeOfCharge: true, + freeOfChargeReason: "test", + dailyCalls: 1, + }; + + await writeInReadmodel(mockPurpose, purposes); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(producer, tenants); + await writeInReadmodel(consumer, tenants); + await writeInReadmodel(mockAgreement, agreements); + + expect( + purposeService.createReversePurpose( + consumer.id, + reversePurposeSeed, + generateId() + ) + ).rejects.toThrowError(duplicatedPurposeTitle(purposeTitle)); + }); it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { const consumer = getMockTenant(); const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; From ed3c12ce62572fdef8274c4e2da2c454c13171e7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 14:11:11 +0200 Subject: [PATCH 386/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1c307f0e71..69a225fba0 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -801,7 +801,7 @@ export function purposeServiceBuilder( ); if (purposeWithSameName) { - throw duplicatedPurposeName(clonedPurposeName); + throw duplicatedPurposeTitle(clonedPurposeName); } const clonedPurpose: Purpose = { From 04cab03662c35bec0ab03b01dce9b5a15920bab3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 14:11:39 +0200 Subject: [PATCH 387/537] Fix --- packages/purpose-process/src/utilities/errorMappers.ts | 2 +- packages/purpose-process/test/testClonePurpose.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 621d704e13..bdc9f59705 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -159,7 +159,7 @@ export const clonePurposeErrorMapper = (error: ApiError): number => .with("tenantKindNotFound", () => HTTP_STATUS_FORBIDDEN) .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) .with( - "duplicatedPurposeName", + "duplicatedPurposeTitle", "purposeCannotBeCloned", () => HTTP_STATUS_CONFLICT ) diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts index 8aa664667c..85a6372e2b 100644 --- a/packages/purpose-process/test/testClonePurpose.ts +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -22,7 +22,7 @@ import { import { describe, expect, it, vi } from "vitest"; import { formatDateAndTime } from "pagopa-interop-commons"; import { - duplicatedPurposeName, + duplicatedPurposeTitle, purposeCannotBeCloned, purposeNotFound, tenantKindNotFound, @@ -263,7 +263,7 @@ export const testClonePurpose = (): ReturnType => correlationId: generateId(), }) ).rejects.toThrowError( - duplicatedPurposeName(mockPurposeWithSameName.title) + duplicatedPurposeTitle(mockPurposeWithSameName.title) ); }); it("should throw tenantKindNotFound if the tenant kind doesn't exist", async () => { From bd5b9284ec96b9370990e8eadf232f622d272648 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 16:22:45 +0200 Subject: [PATCH 388/537] Fix --- .../src/services/purposeService.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 69a225fba0..9441632e49 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1022,17 +1022,15 @@ const getDailyCallsFromPurposeToClone = (purposeToClone: Purpose): number => { ? nonWaitingVersions : purposeToClone.versions; - const sortedVersions = versionsToSearch - .filter((v) => v.state !== purposeVersionState.waitingForApproval) - .sort((v1, v2) => { - if (v1.createdAt > v2.createdAt) { - return -1; - } else if (v1.createdAt < v2.createdAt) { - return 1; - } else { - return 0; - } - }); + const sortedVersions = versionsToSearch.toSorted((v1, v2) => { + if (v1.createdAt > v2.createdAt) { + return -1; + } else if (v1.createdAt < v2.createdAt) { + return 1; + } else { + return 0; + } + }); return sortedVersions.length > 0 ? sortedVersions[0].dailyCalls : 0; }; From 1a7a572d171c693636cb6cfe9e32dd6522333863 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 2 May 2024 16:25:16 +0200 Subject: [PATCH 389/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 9441632e49..1a45bab752 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1022,7 +1022,7 @@ const getDailyCallsFromPurposeToClone = (purposeToClone: Purpose): number => { ? nonWaitingVersions : purposeToClone.versions; - const sortedVersions = versionsToSearch.toSorted((v1, v2) => { + const sortedVersions = versionsToSearch.slice().sort((v1, v2) => { if (v1.createdAt > v2.createdAt) { return -1; } else if (v1.createdAt < v2.createdAt) { From 02daaa39b59b43a61b42c5549af4dead66132e42 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 09:25:18 +0200 Subject: [PATCH 390/537] Refactor --- .../src/services/purposeService.ts | 14 ++++++-------- .../purpose-process/src/services/validators.ts | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a78e44dde7..787d10a8a6 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -4,7 +4,6 @@ import { eventRepository, logger, riskAnalysisFormToRiskAnalysisFormToValidate, - validateRiskAnalysis, } from "pagopa-interop-commons"; import { EService, @@ -50,7 +49,6 @@ import { purposeVersionCannotBeDeleted, purposeVersionDocumentNotFound, purposeVersionNotFound, - riskAnalysisValidationFailed, tenantNotFound, } from "../model/domain/errors.js"; import { @@ -86,6 +84,7 @@ import { isDeletable, isArchivable, isSuspendable, + validateRiskAnalysisOrThrow, } from "./validators.js"; const retrievePurpose = async ( @@ -611,6 +610,7 @@ export function purposeServiceBuilder( const validatedFormSeed = validateAndTransformRiskAnalysis( purposeSeed.riskAnalysisForm, + false, tenant.kind ); @@ -692,7 +692,7 @@ export function purposeServiceBuilder( throw duplicatedPurposeTitle(seed.title); } - const validationResult = validateRiskAnalysis( + validateRiskAnalysisOrThrow( riskAnalysisFormToRiskAnalysisFormToValidate( riskAnalysis.riskAnalysisForm ), @@ -700,10 +700,6 @@ export function purposeServiceBuilder( producer.kind ); - if (validationResult.type === "invalid") { - throw riskAnalysisValidationFailed(validationResult.issues); - } - const newVersion: PurposeVersion = { id: generateId(), createdAt: new Date(), @@ -728,7 +724,7 @@ export function purposeServiceBuilder( await repository.createEvent(event); return { purpose, - isRiskAnalysisValid: validationResult.type === "valid", + isRiskAnalysisValid: true, }; }, }; @@ -866,10 +862,12 @@ const updatePurposeInternal = async ( mode === eserviceMode.deliver ? validateAndTransformRiskAnalysis( (updateContent as ApiPurposeUpdateContent).riskAnalysisForm, + true, tenant.kind ) : reverseValidateAndTransformRiskAnalysis( purpose.data.riskAnalysisForm, + true, tenant.kind ); diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 3463295a6c..79d0c68394 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -77,11 +77,16 @@ export const assertOrganizationIsAConsumer = ( } }; -export function validateRiskAnalysisSchemaOrThrow( +export function validateRiskAnalysisOrThrow( riskAnalysisForm: ApiRiskAnalysisFormSeed, + schemaOnlyValidation: boolean, tenantKind: TenantKind ): RiskAnalysisValidatedForm { - const result = validateRiskAnalysis(riskAnalysisForm, true, tenantKind); + const result = validateRiskAnalysis( + riskAnalysisForm, + schemaOnlyValidation, + tenantKind + ); if (result.type === "invalid") { throw riskAnalysisValidationFailed(result.issues); } else { @@ -91,13 +96,15 @@ export function validateRiskAnalysisSchemaOrThrow( export function validateAndTransformRiskAnalysis( riskAnalysisForm: ApiRiskAnalysisFormSeed | undefined, + schemaOnlyValidation: boolean, tenantKind: TenantKind ): PurposeRiskAnalysisForm | undefined { if (!riskAnalysisForm) { return undefined; } - const validatedForm = validateRiskAnalysisSchemaOrThrow( + const validatedForm = validateRiskAnalysisOrThrow( riskAnalysisForm, + schemaOnlyValidation, tenantKind ); @@ -109,6 +116,7 @@ export function validateAndTransformRiskAnalysis( export function reverseValidateAndTransformRiskAnalysis( riskAnalysisForm: PurposeRiskAnalysisForm | undefined, + schemaOnlyValidation: boolean, tenantKind: TenantKind ): PurposeRiskAnalysisForm | undefined { if (!riskAnalysisForm) { @@ -117,8 +125,9 @@ export function reverseValidateAndTransformRiskAnalysis( const formToValidate = riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm); - const validatedForm = validateRiskAnalysisSchemaOrThrow( + const validatedForm = validateRiskAnalysisOrThrow( formToValidate, + schemaOnlyValidation, tenantKind ); From 3e9fd5872fe61f59c8cf7caba70d3268d690a0d7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 09:28:56 +0200 Subject: [PATCH 391/537] Change error --- packages/purpose-process/src/utilities/errorMappers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 6c5b2476fd..a9e53e8bcb 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -147,8 +147,8 @@ export const createReversePurposeErrorMapper = ( "missingFreeOfChargeReason", "agreementNotFound", "riskAnalysisValidationFailed", + "tenantNotFound", () => HTTP_STATUS_BAD_REQUEST ) .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) - .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 031aa045af1be7ece6eba7dd764fbb348c63aec0 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 09:29:58 +0200 Subject: [PATCH 392/537] Refactor --- packages/purpose-process/src/services/purposeService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 787d10a8a6..6680efd198 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -720,8 +720,9 @@ export function purposeServiceBuilder( riskAnalysisForm: riskAnalysis.riskAnalysisForm, }; - const event = toCreateEventPurposeAdded(purpose, correlationId); - await repository.createEvent(event); + await repository.createEvent( + toCreateEventPurposeAdded(purpose, correlationId) + ); return { purpose, isRiskAnalysisValid: true, From 7c2e618d36688e556439d3e309eedad33ad294f3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 11:57:19 +0200 Subject: [PATCH 393/537] Fix --- packages/models/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index c67eca6247..310a960fc6 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -33,7 +33,6 @@ export * from "./tenant/tenantEvents.js"; export * from "./purpose/purpose.js"; export * from "./purpose/purposeEvents.js"; export * from "./purpose/protobufConverterFromV1.js"; -export * from "./purpose/protobufConverterToV1.js"; export * from "./purpose/protobufConverterFromV2.js"; export * from "./purpose/protobufConverterToV2.js"; From 3d984953c2aface6b8f3a0a4d55de5a03b066e24 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 14:05:34 +0200 Subject: [PATCH 394/537] Minor changes --- .../purpose-process/src/services/purposeService.ts | 12 +++--------- .../purpose-process/src/utilities/errorMappers.ts | 7 +++++-- packages/purpose-process/test/testClonePurpose.ts | 5 ++--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1a45bab752..478e553038 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1022,15 +1022,9 @@ const getDailyCallsFromPurposeToClone = (purposeToClone: Purpose): number => { ? nonWaitingVersions : purposeToClone.versions; - const sortedVersions = versionsToSearch.slice().sort((v1, v2) => { - if (v1.createdAt > v2.createdAt) { - return -1; - } else if (v1.createdAt < v2.createdAt) { - return 1; - } else { - return 0; - } - }); + const sortedVersions = [...versionsToSearch] + .slice() + .sort((v1, v2) => v2.createdAt.getTime() - v1.createdAt.getTime()); return sortedVersions.length > 0 ? sortedVersions[0].dailyCalls : 0; }; diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index bdc9f59705..08deee2572 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -156,8 +156,11 @@ export const createReversePurposeErrorMapper = ( export const clonePurposeErrorMapper = (error: ApiError): number => match(error.code) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("tenantKindNotFound", () => HTTP_STATUS_FORBIDDEN) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) + .with( + "tenantKindNotFound", + "riskAnalysisValidationFailed", + () => HTTP_STATUS_BAD_REQUEST + ) .with( "duplicatedPurposeTitle", "purposeCannotBeCloned", diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts index 85a6372e2b..b07c399d6f 100644 --- a/packages/purpose-process/test/testClonePurpose.ts +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { decodeProtobufPayload, @@ -94,14 +95,12 @@ export const testClonePurpose = (): ReturnType => const expectedPurpose: Purpose = { ...mockPurpose, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion id: unsafeBrandId(writtenPayload.purpose!.id), title: `${mockPurpose.title} - clone - ${formatDateAndTime( new Date() )}`, versions: [ { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion id: unsafeBrandId(writtenPayload.purpose!.versions[0].id), state: purposeVersionState.draft, createdAt: new Date(), @@ -219,7 +218,7 @@ export const testClonePurpose = (): ReturnType => }) ).rejects.toThrowError(purposeCannotBeCloned(mockPurpose.id)); }); - it("should throw duplicatedPurposeName if a purpose with the same name already exists", async () => { + it("should throw duplicatedPurposeTitle if a purpose with the same name already exists", async () => { const mockTenant = { ...getMockTenant(), kind: tenantKind.PA, From 2d6e886e69797d1679dd982c9eb5b10c099707a4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 14:11:27 +0200 Subject: [PATCH 395/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a99046261b..9ff083bbd6 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -5,6 +5,7 @@ import { formatDateAndTime, logger, riskAnalysisFormToRiskAnalysisFormToValidate, + validateRiskAnalysis, } from "pagopa-interop-commons"; import { EService, From 08405666057699a5380155bb72e31352800ca44d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 3 May 2024 14:50:58 +0200 Subject: [PATCH 396/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 9ff083bbd6..353e1aa2da 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1022,9 +1022,9 @@ const getDailyCallsFromPurposeToClone = (purposeToClone: Purpose): number => { ? nonWaitingVersions : purposeToClone.versions; - const sortedVersions = [...versionsToSearch] - .slice() - .sort((v1, v2) => v2.createdAt.getTime() - v1.createdAt.getTime()); + const sortedVersions = [...versionsToSearch].sort( + (v1, v2) => v2.createdAt.getTime() - v1.createdAt.getTime() + ); return sortedVersions.length > 0 ? sortedVersions[0].dailyCalls : 0; }; From 648b0f47cb5ae1f8b71aa817dc76e80ed7ed628b Mon Sep 17 00:00:00 2001 From: AsterITA Date: Fri, 3 May 2024 18:10:57 +0200 Subject: [PATCH 397/537] WIP --- .../models/src/risk-analysis/riskAnalysis.ts | 75 +++++++++++++++ .../src/model/domain/apiConverter.ts | 94 +++++++++++++++++++ .../src/model/domain/models.ts | 19 ++++ .../src/routers/PurposeRouter.ts | 25 ++++- .../src/services/purposeService.ts | 8 ++ 5 files changed, 220 insertions(+), 1 deletion(-) diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index f1c8251782..1e9a28ae4a 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -43,3 +43,78 @@ export const RiskAnalysis = z.object({ createdAt: z.coerce.date(), }); export type RiskAnalysis = z.infer; + +export const LocalizedText = z.object({ + it: z.string(), + en: z.string(), +}); +export type LocalizedText = z.infer; + +export const dataType = { + single: "SINGLE", + multi: "MULTI", + freetext: "FREETEXT", +} as const; +export const DataType = z.enum([ + Object.values(dataType)[0], + ...Object.values(dataType).slice(1), +]); +export type DataType = z.infer; + +export const Dependency = z.object({ + id: z.string(), + value: z.string(), +}); +export type Dependency = z.infer; + +export const HideOptionConfig = z.object({ + id: z.string(), + value: z.string(), +}); +export type HideOptionConfig = z.infer; + +export const ValidationOption = z.object({ + maxLength: z.number().optional(), +}); +export type ValidationOption = z.infer; + +export const FreeInputQuestion = z.object({ + id: z.string(), + label: LocalizedText, + infoLabel: LocalizedText.optional(), + dataType: DataType, + required: z.boolean(), + dependencies: z.array(Dependency), + type: z.string(), + defaultValue: z.array(z.string()), + hideOption: z.record(z.string(), z.array(HideOptionConfig)).optional(), + validation: ValidationOption.optional(), +}); +export type FreeInputQuestion = z.infer; + +export const LabeledValue = z.object({ + label: LocalizedText, + value: z.string(), +}); +export type LabeledValue = z.infer; + +export const SingleQuestion = FreeInputQuestion.extend({ + options: z.array(LabeledValue), +}); +export type SingleQuestion = z.infer; + +export const MultiQuestion = SingleQuestion; +export type MultiQuestion = z.infer; + +export const FormConfigQuestion = z.discriminatedUnion("dataType", [ + FreeInputQuestion, + SingleQuestion, + MultiQuestion, +]); +export type FormConfigQuestion = z.infer; + +export const RiskAnalysisFormConfig = z.object({ + version: z.string(), + questions: z.array(FormConfigQuestion), +}); +export type RiskAnalysisFormConfig = z.infer; diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index 421f8c2121..fd4e3efd98 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -8,13 +8,26 @@ import { RiskAnalysisMultiAnswer, RiskAnalysisSingleAnswer, purposeVersionState, + RiskAnalysisFormConfig, + FormConfigQuestion, + LocalizedText, + dataType, + DataType, + Dependency, + HideOptionConfig, } from "pagopa-interop-models"; import { + ApiDataType, + ApiDependency, + ApiFormConfigQuestion, + ApiHideOptionConfig, + ApiLocalizedText, ApiPurpose, ApiPurposeVersion, ApiPurposeVersionDocument, ApiPurposeVersionState, ApiRiskAnalysisForm, + ApiRiskAnalysisFormConfig, } from "./models.js"; export const singleAnswersToApiSingleAnswers = ( @@ -127,3 +140,84 @@ export const purposeToApiPurpose = ( isFreeOfCharge: purpose.isFreeOfCharge, freeOfChargeReason: purpose.freeOfChargeReason, }); + +export const localizedTextToApiLocalizedText = ( + localizedText: LocalizedText +): ApiLocalizedText => ({ + it: localizedText.it, + en: localizedText.en, +}); + +export const dataTypeToApiDataType = (type: DataType): ApiDataType => + match(type) + .with(dataType.single, () => "SINGLE") + .with(dataType.multi, () => "MULTI") + .with(dataType.freetext, () => "FREETEXT") + .exhaustive(); + +export const dependencyToApiDependency = ( + dependency: Dependency +): ApiDependency => ({ + id: dependency.id, + value: dependency.value, +}); + +export const hideOptionConfigToApiHideOptionConfig = ( + hideOptionConfig: HideOptionConfig +): ApiHideOptionConfig => ({ + id: hideOptionConfig.id, + value: hideOptionConfig.value, +}); +export const mapHideOptionToApiMapHideOption = ( + mapHideOptionConfig: Record +): Record => + Object.fromEntries( + Object.entries(mapHideOptionConfig).map(([key, value]) => [ + key, + value.map(hideOptionConfigToApiHideOptionConfig), + ]) + ); + +export const formConfigQuestiontoApiFormConfigQuestion = ( + question: FormConfigQuestion +): ApiFormConfigQuestion => + match(question) + .with({ dataType: dataType.freetext }, (q) => ({ + id: q.id, + label: localizedTextToApiLocalizedText(q.label), + infoLabel: q.infoLabel + ? localizedTextToApiLocalizedText(q.infoLabel) + : undefined, + dataType: dataTypeToApiDataType(q.dataType), + required: q.required, + dependencies: q.dependencies.map(dependencyToApiDependency), + visualType: q.type, + defaultValue: q.defaultValue, + hideOption: q.hideOption + ? mapHideOptionToApiMapHideOption(q.hideOption) + : undefined, + })) + .with({ dataType: dataType.single }, (q) => ({ + id: q.id, + label: localizedTextToApiLocalizedText(q.label), + infoLabel: q.infoLabel + ? localizedTextToApiLocalizedText(q.infoLabel) + : undefined, + dataType: dataTypeToApiDataType(q.dataType), + required: q.required, + dependencies: q.dependencies.map(dependencyToApiDependency), + visualType: q.type, + defaultValue: q.defaultValue, + hideOption: q.hideOption + ? mapHideOptionToApiMapHideOption(q.hideOption) + : undefined, + options: q.options.map(), + })) + .with({ dataType: dataType.multi }, (q) => ({})) + .exhaustive(); +export const riskAnalysisFormConfigToApiRiskAnalysisFormConfig = ( + configuration: RiskAnalysisFormConfig +): ApiRiskAnalysisFormConfig => ({ + version: configuration.version, + answers: configuration.answers.map(formConfigQuestiontoApiFormConfigQuestion), +}); diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 0d2738310f..1575efc674 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -44,3 +44,22 @@ export type ApiReversePurposeSeed = z.infer< >; export type ApiPurposeCloneSeed = z.infer; + +export type ApiFormConfigQuestion = z.infer< + typeof api.schemas.FormConfigQuestionResponse +>; +export type ApiRiskAnalysisFormConfig = z.infer< + typeof api.schemas.RiskAnalysisFormConfigResponse +>; + +export type ApiLocalizedText = z.infer< + typeof api.schemas.LocalizedTextResponse +>; + +export type ApiDataType = z.infer; + +export type ApiDependency = z.infer; + +export type ApiHideOptionConfig = z.infer< + typeof api.schemas.HideOptionResponse +>; diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index bf26f3aa64..c545a75444 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -408,7 +408,30 @@ const purposeRouter = ( .get( "/purposes/riskAnalysis/version/:riskAnalysisVersion", authorizationMiddleware([ADMIN_ROLE, SUPPORT_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const riskAnalysisConfiguration = + await purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: req.query.eserviceId, + riskAnalysisVersion: req.params.riskAnalysisVersion, + organizationId: req.ctx.authData.organizationId, + }); + return res + .status(200) + .json( + riskAnalysisConfigurationToApiRiskAnalysisConfiguration( + riskAnalysisConfiguration + ) + ) + .end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + getRiskAnalysisDocumentErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ); return purposeRouter; diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 353e1aa2da..74ebd0e3ae 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -34,6 +34,7 @@ import { Agreement, RiskAnalysisId, RiskAnalysis, + RiskAnalysisFormConfig, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -837,6 +838,13 @@ export function purposeServiceBuilder( isRiskAnalysisValid, }; }, + async retrieveRiskAnalysisConfigurationByVersion( + eserviceId: EServiceId, + riskAnalysisVersion: string, + correlationId: string + ): Promise { + return any; + }, }; } From b5483e1fabd0095909dce2912e0d9b0c0622f329 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 6 May 2024 09:53:45 +0200 Subject: [PATCH 398/537] Fix import --- packages/commons-test/src/testUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 556d2c2bc2..7a67714e8d 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -22,7 +22,6 @@ import { agreementState, descriptorState, generateId, - tenantAttributeType, } from "pagopa-interop-models"; import { v4 as uuidv4 } from "uuid"; import { AuthData } from "pagopa-interop-commons"; From 3b94a6bc8c98c81cac2e6b11a6f6e3b7b0146bbd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 6 May 2024 09:54:14 +0200 Subject: [PATCH 399/537] Fix import --- packages/commons-test/src/testUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 07d5402fed..fc2d4cd4be 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -23,7 +23,6 @@ import { agreementState, descriptorState, generateId, - tenantAttributeType, purposeVersionState, } from "pagopa-interop-models"; import { v4 as uuidv4 } from "uuid"; From bb23fcd8406dea71f5517ea6b734ac23746242b7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 6 May 2024 09:57:37 +0200 Subject: [PATCH 400/537] Fix import --- packages/purpose-process/src/services/purposeService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 13b971bdd0..1bc038ab57 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -15,6 +15,7 @@ import { PurposeVersionId, ownership, purposeEventToBinaryData, + purposeVersionState, } from "pagopa-interop-models"; import { eserviceNotFound, From a3e91660a763caf4427b36ee8f68a3d2a862e112 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 6 May 2024 11:55:56 +0200 Subject: [PATCH 401/537] implemented endpoint --- packages/commons/src/risk-analysis/index.ts | 2 + .../src/risk-analysis/riskAnalysisService.ts | 35 ++++++++ .../riskAnalysisTemplate.ts | 79 +++++++++++++++++++ .../models/src/risk-analysis/riskAnalysis.ts | 75 ------------------ .../src/model/domain/apiConverter.ts | 72 +++++++++-------- .../src/model/domain/errors.ts | 24 ++++++ .../src/model/domain/models.ts | 2 + .../src/routers/PurposeRouter.ts | 10 ++- .../src/services/purposeService.ts | 47 +++++++++-- .../src/utilities/errorMappers.ts | 17 ++++ 10 files changed, 243 insertions(+), 120 deletions(-) create mode 100644 packages/commons/src/risk-analysis/riskAnalysisService.ts create mode 100644 packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts diff --git a/packages/commons/src/risk-analysis/index.ts b/packages/commons/src/risk-analysis/index.ts index 89a5cc6c1e..b546263708 100644 --- a/packages/commons/src/risk-analysis/index.ts +++ b/packages/commons/src/risk-analysis/index.ts @@ -1,3 +1,5 @@ export * from "./models.js"; export * from "./riskAnalysisErrors.js"; export * from "./riskAnalysisValidation.js"; +export * from "./riskAnalysisService.js"; +export * from "./riskAnalysisTemplate/riskAnalysisTemplate.js"; diff --git a/packages/commons/src/risk-analysis/riskAnalysisService.ts b/packages/commons/src/risk-analysis/riskAnalysisService.ts new file mode 100644 index 0000000000..ed6f60881b --- /dev/null +++ b/packages/commons/src/risk-analysis/riskAnalysisService.ts @@ -0,0 +1,35 @@ +import { readFileSync } from "fs"; +import { join } from "path"; +import { TenantKind, tenantKind } from "pagopa-interop-models"; +import { RiskAnalysisFormConfig } from "./riskAnalysisTemplate/riskAnalysisTemplate.js"; + +type RiskAnalysisFormConfigs = { [key: string]: RiskAnalysisFormConfig }; +type RiskAnalysisFormsMap = { + [key in TenantKind]: RiskAnalysisFormConfigs; +}; + +const loadRiskAnalysisFormConfig = (path: string): RiskAnalysisFormConfig => { + const riskAnalysisTemplatePath: string = "riskAnalysisTemplate/forms"; + + const configContent = readFileSync( + join(riskAnalysisTemplatePath, path), + "utf8" + ); + return RiskAnalysisFormConfig.parse(configContent); +}; + +export const riskAnalysisFormsMap: RiskAnalysisFormsMap = { + [tenantKind.PA]: { + "1.0": loadRiskAnalysisFormConfig(join(tenantKind.PA, "1.0.json")), + "2.0": loadRiskAnalysisFormConfig(join(tenantKind.PA, "2.0.json")), + "3.0": loadRiskAnalysisFormConfig(join(tenantKind.PA, "3.0.json")), + }, + [tenantKind.PRIVATE]: { + "1.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "1.0.json")), + "2.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "2.0.json")), + }, + [tenantKind.GSP]: { + "1.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "1.0.json")), + "2.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "2.0.json")), + }, +}; diff --git a/packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts b/packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts new file mode 100644 index 0000000000..c864d99a4d --- /dev/null +++ b/packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts @@ -0,0 +1,79 @@ +import { z } from "zod"; + +export const LocalizedText = z.object({ + it: z.string(), + en: z.string(), +}); +export type LocalizedText = z.infer; + +export const dataType = { + single: "SINGLE", + multi: "MULTI", + freetext: "FREETEXT", +} as const; +export const DataType = z.enum([ + Object.values(dataType)[0], + ...Object.values(dataType).slice(1), +]); +export type DataType = z.infer; + +export const Dependency = z.object({ + id: z.string(), + value: z.string(), +}); +export type Dependency = z.infer; + +export const HideOptionConfig = z.object({ + id: z.string(), + value: z.string(), +}); +export type HideOptionConfig = z.infer; + +export const ValidationOption = z.object({ + maxLength: z.number().optional(), +}); +export type ValidationOption = z.infer; + +export const FreeInputQuestion = z.object({ + id: z.string(), + label: LocalizedText, + infoLabel: LocalizedText.optional(), + dataType: z.literal(dataType.freetext), + required: z.boolean(), + dependencies: z.array(Dependency), + type: z.string(), + defaultValue: z.array(z.string()), + hideOption: z.record(z.string(), z.array(HideOptionConfig)).optional(), + validation: ValidationOption.optional(), +}); +export type FreeInputQuestion = z.infer; + +export const LabeledValue = z.object({ + label: LocalizedText, + value: z.string(), +}); +export type LabeledValue = z.infer; + +export const SingleQuestion = FreeInputQuestion.extend({ + dataType: z.literal(dataType.single), + options: z.array(LabeledValue), +}); +export type SingleQuestion = z.infer; + +export const MultiQuestion = SingleQuestion.extend({ + dataType: z.literal(dataType.multi), +}); +export type MultiQuestion = z.infer; + +export const FormConfigQuestion = z.discriminatedUnion("dataType", [ + FreeInputQuestion, + SingleQuestion, + MultiQuestion, +]); +export type FormConfigQuestion = z.infer; + +export const RiskAnalysisFormConfig = z.object({ + version: z.string(), + questions: z.array(FormConfigQuestion), +}); +export type RiskAnalysisFormConfig = z.infer; diff --git a/packages/models/src/risk-analysis/riskAnalysis.ts b/packages/models/src/risk-analysis/riskAnalysis.ts index 1e9a28ae4a..f1c8251782 100644 --- a/packages/models/src/risk-analysis/riskAnalysis.ts +++ b/packages/models/src/risk-analysis/riskAnalysis.ts @@ -43,78 +43,3 @@ export const RiskAnalysis = z.object({ createdAt: z.coerce.date(), }); export type RiskAnalysis = z.infer; - -export const LocalizedText = z.object({ - it: z.string(), - en: z.string(), -}); -export type LocalizedText = z.infer; - -export const dataType = { - single: "SINGLE", - multi: "MULTI", - freetext: "FREETEXT", -} as const; -export const DataType = z.enum([ - Object.values(dataType)[0], - ...Object.values(dataType).slice(1), -]); -export type DataType = z.infer; - -export const Dependency = z.object({ - id: z.string(), - value: z.string(), -}); -export type Dependency = z.infer; - -export const HideOptionConfig = z.object({ - id: z.string(), - value: z.string(), -}); -export type HideOptionConfig = z.infer; - -export const ValidationOption = z.object({ - maxLength: z.number().optional(), -}); -export type ValidationOption = z.infer; - -export const FreeInputQuestion = z.object({ - id: z.string(), - label: LocalizedText, - infoLabel: LocalizedText.optional(), - dataType: DataType, - required: z.boolean(), - dependencies: z.array(Dependency), - type: z.string(), - defaultValue: z.array(z.string()), - hideOption: z.record(z.string(), z.array(HideOptionConfig)).optional(), - validation: ValidationOption.optional(), -}); -export type FreeInputQuestion = z.infer; - -export const LabeledValue = z.object({ - label: LocalizedText, - value: z.string(), -}); -export type LabeledValue = z.infer; - -export const SingleQuestion = FreeInputQuestion.extend({ - options: z.array(LabeledValue), -}); -export type SingleQuestion = z.infer; - -export const MultiQuestion = SingleQuestion; -export type MultiQuestion = z.infer; - -export const FormConfigQuestion = z.discriminatedUnion("dataType", [ - FreeInputQuestion, - SingleQuestion, - MultiQuestion, -]); -export type FormConfigQuestion = z.infer; - -export const RiskAnalysisFormConfig = z.object({ - version: z.string(), - questions: z.array(FormConfigQuestion), -}); -export type RiskAnalysisFormConfig = z.infer; diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index fd4e3efd98..a6a1bba960 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -15,12 +15,14 @@ import { DataType, Dependency, HideOptionConfig, + LabeledValue, } from "pagopa-interop-models"; import { ApiDataType, ApiDependency, ApiFormConfigQuestion, ApiHideOptionConfig, + ApiLabeledValue, ApiLocalizedText, ApiPurpose, ApiPurposeVersion, @@ -178,46 +180,46 @@ export const mapHideOptionToApiMapHideOption = ( ]) ); -export const formConfigQuestiontoApiFormConfigQuestion = ( +export const labeledValueToApiLabeledValue = ( + labeledValue: LabeledValue +): ApiLabeledValue => ({ + label: localizedTextToApiLocalizedText(labeledValue.label), + value: labeledValue.value, +}); + +export const formConfigQuestionToApiFormConfigQuestion = ( question: FormConfigQuestion -): ApiFormConfigQuestion => - match(question) - .with({ dataType: dataType.freetext }, (q) => ({ - id: q.id, - label: localizedTextToApiLocalizedText(q.label), - infoLabel: q.infoLabel - ? localizedTextToApiLocalizedText(q.infoLabel) - : undefined, - dataType: dataTypeToApiDataType(q.dataType), - required: q.required, - dependencies: q.dependencies.map(dependencyToApiDependency), - visualType: q.type, - defaultValue: q.defaultValue, - hideOption: q.hideOption - ? mapHideOptionToApiMapHideOption(q.hideOption) - : undefined, - })) - .with({ dataType: dataType.single }, (q) => ({ - id: q.id, - label: localizedTextToApiLocalizedText(q.label), - infoLabel: q.infoLabel - ? localizedTextToApiLocalizedText(q.infoLabel) - : undefined, - dataType: dataTypeToApiDataType(q.dataType), - required: q.required, - dependencies: q.dependencies.map(dependencyToApiDependency), - visualType: q.type, - defaultValue: q.defaultValue, - hideOption: q.hideOption - ? mapHideOptionToApiMapHideOption(q.hideOption) - : undefined, - options: q.options.map(), +): ApiFormConfigQuestion => { + const commonFields = { + id: question.id, + label: localizedTextToApiLocalizedText(question.label), + infoLabel: question.infoLabel + ? localizedTextToApiLocalizedText(question.infoLabel) + : undefined, + dataType: dataTypeToApiDataType(question.dataType), + required: question.required, + dependencies: question.dependencies.map(dependencyToApiDependency), + visualType: question.type, + defaultValue: question.defaultValue, + hideOption: question.hideOption + ? mapHideOptionToApiMapHideOption(question.hideOption) + : undefined, + }; + + return match(question) + .with({ dataType: dataType.freetext }, () => commonFields) + .with({ dataType: dataType.single }, { dataType: dataType.multi }, (q) => ({ + ...commonFields, + options: q.options.map(labeledValueToApiLabeledValue), })) - .with({ dataType: dataType.multi }, (q) => ({})) .exhaustive(); +}; + export const riskAnalysisFormConfigToApiRiskAnalysisFormConfig = ( configuration: RiskAnalysisFormConfig ): ApiRiskAnalysisFormConfig => ({ version: configuration.version, - answers: configuration.answers.map(formConfigQuestiontoApiFormConfigQuestion), + questions: configuration.questions.map( + formConfigQuestionToApiFormConfigQuestion + ), }); diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index d81444963a..281fa2beff 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -8,6 +8,7 @@ import { PurposeVersionState, RiskAnalysisId, TenantId, + TenantKind, makeApiProblemBuilder, } from "pagopa-interop-models"; import { RiskAnalysisValidationIssue, logger } from "pagopa-interop-commons"; @@ -34,6 +35,8 @@ export const errorCodes = { agreementNotFound: "0019", eserviceRiskAnalysisNotFound: "0020", purposeCannotBeCloned: "0021", + riskAnalysisConfigVersionNotFound: "0022", + riskAnalysisConfigForTenantKindNotFound: "0023", }; export type ErrorCodes = keyof typeof errorCodes; @@ -243,3 +246,24 @@ export function purposeCannotBeCloned( title: "Purpose cannot be cloned", }); } + +export function RiskAnalysisConfigVersionNotFound( + version: string, + tenantKind: TenantKind +): ApiError { + return new ApiError({ + detail: `Risk Analysis Configuration version ${version} for tenant kind ${tenantKind} not found`, + code: "riskAnalysisConfigVersionNotFound", + title: "Risk Analysis config version not found", + }); +} + +export function RiskAnalysisConfigForTenantKindNotFound( + tenantId: TenantId +): ApiError { + return new ApiError({ + detail: `Risk Analysis Configuration for tenant ${tenantId} not found`, + code: "riskAnalysisConfigForTenantKindNotFound", + title: "Risk Analysis config for tenant kind not found", + }); +} diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 1575efc674..70086887b1 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -63,3 +63,5 @@ export type ApiDependency = z.infer; export type ApiHideOptionConfig = z.infer< typeof api.schemas.HideOptionResponse >; + +export type ApiLabeledValue = z.infer; diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index c545a75444..f6e21e37ab 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -16,10 +16,11 @@ import { purposeToApiPurpose, purposeVersionDocumentToApiPurposeVersionDocument, purposeVersionToApiPurposeVersion, + riskAnalysisFormConfigToApiRiskAnalysisFormConfig, } from "../model/domain/apiConverter.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../utilities/config.js"; -import { purposeServiceBuilder } from "../services/purposeService.js"; +import { purposeServiceBuilder } from "../services/purposeServiceBuilder.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { archivePurposeVersionErrorMapper, @@ -31,6 +32,7 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, + retrieveRiskAnalysisConfigurationByVersionErrorMapper, suspendPurposeVersionErrorMapper, updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; @@ -412,14 +414,14 @@ const purposeRouter = ( try { const riskAnalysisConfiguration = await purposeService.retrieveRiskAnalysisConfigurationByVersion({ - eserviceId: req.query.eserviceId, + eserviceId: unsafeBrandId(req.query.eserviceId), riskAnalysisVersion: req.params.riskAnalysisVersion, organizationId: req.ctx.authData.organizationId, }); return res .status(200) .json( - riskAnalysisConfigurationToApiRiskAnalysisConfiguration( + riskAnalysisFormConfigToApiRiskAnalysisFormConfig( riskAnalysisConfiguration ) ) @@ -427,7 +429,7 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - getRiskAnalysisDocumentErrorMapper + retrieveRiskAnalysisConfigurationByVersionErrorMapper ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 74ebd0e3ae..d75f946848 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -5,6 +5,7 @@ import { formatDateAndTime, logger, riskAnalysisFormToRiskAnalysisFormToValidate, + riskAnalysisFormsMap, validateRiskAnalysis, } from "pagopa-interop-commons"; import { @@ -54,6 +55,8 @@ import { purposeVersionDocumentNotFound, purposeVersionNotFound, tenantNotFound, + RiskAnalysisConfigVersionNotFound, + RiskAnalysisConfigForTenantKindNotFound, } from "../model/domain/errors.js"; import { toCreateEventDraftPurposeDeleted, @@ -838,12 +841,44 @@ export function purposeServiceBuilder( isRiskAnalysisValid, }; }, - async retrieveRiskAnalysisConfigurationByVersion( - eserviceId: EServiceId, - riskAnalysisVersion: string, - correlationId: string - ): Promise { - return any; + async retrieveRiskAnalysisConfigurationByVersion({ + eserviceId, + riskAnalysisVersion, + organizationId, + }: { + eserviceId: EServiceId; + riskAnalysisVersion: string; + organizationId: TenantId; + }): Promise { + logger.info( + `Retrieve version ${riskAnalysisVersion} of risk analysis configuration` + ); + + const eservice = await retrieveEService(eserviceId, readModelService); + const tenant = await getInvolvedTenantByEServiceMode( + eservice, + organizationId, + readModelService + ); + + assertTenantKindExists(tenant); + + const kindConfig = riskAnalysisFormsMap[tenant.kind]; + + if (!kindConfig) { + throw RiskAnalysisConfigForTenantKindNotFound(tenant.id); + } + + const riskAnalysisFormConfig = kindConfig[riskAnalysisVersion]; + + if (!riskAnalysisFormConfig) { + throw RiskAnalysisConfigVersionNotFound( + riskAnalysisVersion, + tenant.kind + ); + } + + return riskAnalysisFormConfig; }, }; } diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index c3b8792b2f..cf7a35b5e5 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -168,3 +168,20 @@ export const clonePurposeErrorMapper = (error: ApiError): number => ) .with("tenantNotFound", () => HTTP_STATUS_INTERNAL_SERVER_ERROR) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const retrieveRiskAnalysisConfigurationByVersionErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "eserviceNotFound", + "riskAnalysisConfigVersionNotFound", + () => HTTP_STATUS_NOT_FOUND + ) + .with( + "tenantNotFound", + "tenantKindNotFound", + "riskAnalysisConfigForTenantKindNotFound", + () => HTTP_STATUS_BAD_REQUEST + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 4def5fa346bc02a59838faf799c21fa62457e501 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 6 May 2024 12:10:27 +0200 Subject: [PATCH 402/537] fix update title --- .../src/services/purposeService.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 65273e4ef5..2ace95494d 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -427,16 +427,17 @@ const updatePurposeInternal = async ( assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); assertPurposeIsDraft(purpose.data); - const purposeWithSameTitle = await readModelService.getSpecificPurpose( - purpose.data.eserviceId, - purpose.data.consumerId, - updateContent.title - ); + if (updateContent.title !== purpose.data.title) { + const purposeWithSameTitle = await readModelService.getSpecificPurpose( + purpose.data.eserviceId, + purpose.data.consumerId, + updateContent.title + ); - if (purposeWithSameTitle) { - throw duplicatedPurposeTitle(updateContent.title); + if (purposeWithSameTitle) { + throw duplicatedPurposeTitle(updateContent.title); + } } - const eservice = await retrieveEService( purpose.data.eserviceId, readModelService From fcbf012a5eb4979d0d4a2f1d8d54f4a8666eff28 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 6 May 2024 12:19:36 +0200 Subject: [PATCH 403/537] updated tests --- .../purpose-process/test/testUpdatePurpose.ts | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts index 0cb9cab8ab..8e1950c6c6 100644 --- a/packages/purpose-process/test/testUpdatePurpose.ts +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -116,7 +116,7 @@ export const testUpdatePurpose = (): ReturnType => ...purposeUpdateContent, }; - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER", async () => { + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER (including title change)", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); @@ -159,7 +159,56 @@ export const testUpdatePurpose = (): ReturnType => expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); vi.useRealTimers(); }); - it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE", async () => { + + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER (no title change)", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + await addOnePurpose(purposeForDeliver, postgresDB, purposes); + await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); + await writeInReadmodel(tenant, tenants); + + const updateContentWithoutTitle = { + ...purposeUpdateContent, + title: purposeForDeliver.title, + }; + + await purposeService.updatePurpose({ + purposeId: purposeForDeliver.id, + purposeUpdateContent: updateContentWithoutTitle, + organizationId: tenant.id, + correlationId: generateId(), + }); + + const writtenEvent = await readLastEventByStreamId( + purposeForDeliver.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: purposeForDeliver.id, + version: "1", + type: "DraftPurposeUpdated", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: DraftPurposeUpdatedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = createUpdatedPurpose( + purposeForDeliver, + updateContentWithoutTitle, + validRiskAnalysis, + writtenPayload.purpose!.riskAnalysisForm! + ); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + vi.useRealTimers(); + }); + it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE (including title change)", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From 7ddd82458ee8e52bdc11d8bfea4ae388730b7544 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 6 May 2024 12:43:46 +0200 Subject: [PATCH 404/537] fixed implementation by reusing already implemented code --- packages/commons/src/risk-analysis/index.ts | 4 +- .../src/risk-analysis/riskAnalysisService.ts | 35 -------- .../riskAnalysisTemplate.ts | 79 ------------------- .../rules/riskAnalysisFormRules.ts | 4 + .../src/model/domain/apiConverter.ts | 28 ++++--- .../src/model/domain/models.ts | 4 +- .../src/services/purposeService.ts | 8 +- 7 files changed, 28 insertions(+), 134 deletions(-) delete mode 100644 packages/commons/src/risk-analysis/riskAnalysisService.ts delete mode 100644 packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts diff --git a/packages/commons/src/risk-analysis/index.ts b/packages/commons/src/risk-analysis/index.ts index b546263708..c6af0acd2a 100644 --- a/packages/commons/src/risk-analysis/index.ts +++ b/packages/commons/src/risk-analysis/index.ts @@ -1,5 +1,5 @@ export * from "./models.js"; export * from "./riskAnalysisErrors.js"; export * from "./riskAnalysisValidation.js"; -export * from "./riskAnalysisService.js"; -export * from "./riskAnalysisTemplate/riskAnalysisTemplate.js"; +export * from "./rules/riskAnalysisFormRules.js"; +export * from "./rules/riskAnalysisFormRulesProvider.js"; diff --git a/packages/commons/src/risk-analysis/riskAnalysisService.ts b/packages/commons/src/risk-analysis/riskAnalysisService.ts deleted file mode 100644 index ed6f60881b..0000000000 --- a/packages/commons/src/risk-analysis/riskAnalysisService.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { readFileSync } from "fs"; -import { join } from "path"; -import { TenantKind, tenantKind } from "pagopa-interop-models"; -import { RiskAnalysisFormConfig } from "./riskAnalysisTemplate/riskAnalysisTemplate.js"; - -type RiskAnalysisFormConfigs = { [key: string]: RiskAnalysisFormConfig }; -type RiskAnalysisFormsMap = { - [key in TenantKind]: RiskAnalysisFormConfigs; -}; - -const loadRiskAnalysisFormConfig = (path: string): RiskAnalysisFormConfig => { - const riskAnalysisTemplatePath: string = "riskAnalysisTemplate/forms"; - - const configContent = readFileSync( - join(riskAnalysisTemplatePath, path), - "utf8" - ); - return RiskAnalysisFormConfig.parse(configContent); -}; - -export const riskAnalysisFormsMap: RiskAnalysisFormsMap = { - [tenantKind.PA]: { - "1.0": loadRiskAnalysisFormConfig(join(tenantKind.PA, "1.0.json")), - "2.0": loadRiskAnalysisFormConfig(join(tenantKind.PA, "2.0.json")), - "3.0": loadRiskAnalysisFormConfig(join(tenantKind.PA, "3.0.json")), - }, - [tenantKind.PRIVATE]: { - "1.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "1.0.json")), - "2.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "2.0.json")), - }, - [tenantKind.GSP]: { - "1.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "1.0.json")), - "2.0": loadRiskAnalysisFormConfig(join(tenantKind.PRIVATE, "2.0.json")), - }, -}; diff --git a/packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts b/packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts deleted file mode 100644 index c864d99a4d..0000000000 --- a/packages/commons/src/risk-analysis/riskAnalysisTemplate/riskAnalysisTemplate.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { z } from "zod"; - -export const LocalizedText = z.object({ - it: z.string(), - en: z.string(), -}); -export type LocalizedText = z.infer; - -export const dataType = { - single: "SINGLE", - multi: "MULTI", - freetext: "FREETEXT", -} as const; -export const DataType = z.enum([ - Object.values(dataType)[0], - ...Object.values(dataType).slice(1), -]); -export type DataType = z.infer; - -export const Dependency = z.object({ - id: z.string(), - value: z.string(), -}); -export type Dependency = z.infer; - -export const HideOptionConfig = z.object({ - id: z.string(), - value: z.string(), -}); -export type HideOptionConfig = z.infer; - -export const ValidationOption = z.object({ - maxLength: z.number().optional(), -}); -export type ValidationOption = z.infer; - -export const FreeInputQuestion = z.object({ - id: z.string(), - label: LocalizedText, - infoLabel: LocalizedText.optional(), - dataType: z.literal(dataType.freetext), - required: z.boolean(), - dependencies: z.array(Dependency), - type: z.string(), - defaultValue: z.array(z.string()), - hideOption: z.record(z.string(), z.array(HideOptionConfig)).optional(), - validation: ValidationOption.optional(), -}); -export type FreeInputQuestion = z.infer; - -export const LabeledValue = z.object({ - label: LocalizedText, - value: z.string(), -}); -export type LabeledValue = z.infer; - -export const SingleQuestion = FreeInputQuestion.extend({ - dataType: z.literal(dataType.single), - options: z.array(LabeledValue), -}); -export type SingleQuestion = z.infer; - -export const MultiQuestion = SingleQuestion.extend({ - dataType: z.literal(dataType.multi), -}); -export type MultiQuestion = z.infer; - -export const FormConfigQuestion = z.discriminatedUnion("dataType", [ - FreeInputQuestion, - SingleQuestion, - MultiQuestion, -]); -export type FormConfigQuestion = z.infer; - -export const RiskAnalysisFormConfig = z.object({ - version: z.string(), - questions: z.array(FormConfigQuestion), -}); -export type RiskAnalysisFormConfig = z.infer; diff --git a/packages/commons/src/risk-analysis/rules/riskAnalysisFormRules.ts b/packages/commons/src/risk-analysis/rules/riskAnalysisFormRules.ts index e15a3d46a6..90cc028275 100644 --- a/packages/commons/src/risk-analysis/rules/riskAnalysisFormRules.ts +++ b/packages/commons/src/risk-analysis/rules/riskAnalysisFormRules.ts @@ -15,16 +15,19 @@ const LocalizedText = z.object({ it: z.string(), en: z.string(), }); +export type LocalizedText = z.infer; const Dependency = z.object({ id: z.string(), value: z.string(), }); +export type Dependency = z.infer; const HideOptionConfig = z.object({ id: z.string(), value: z.string(), }); +export type HideOptionConfig = z.infer; const ValidationOption = z.object({ maxLength: z.number().optional(), @@ -34,6 +37,7 @@ const LabeledValue = z.object({ label: LocalizedText, value: z.string(), }); +export type LabeledValue = z.infer; const FormConfigQuestionCommonProps = z.object({ id: z.string(), diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index a6a1bba960..a662f9c649 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -8,19 +8,21 @@ import { RiskAnalysisMultiAnswer, RiskAnalysisSingleAnswer, purposeVersionState, - RiskAnalysisFormConfig, - FormConfigQuestion, +} from "pagopa-interop-models"; +import { LocalizedText, - dataType, DataType, + dataType, Dependency, HideOptionConfig, LabeledValue, -} from "pagopa-interop-models"; + FormQuestionRules, + RiskAnalysisFormRules, +} from "pagopa-interop-commons"; import { ApiDataType, ApiDependency, - ApiFormConfigQuestion, + ApiFormQuestionRules, ApiHideOptionConfig, ApiLabeledValue, ApiLocalizedText, @@ -29,7 +31,7 @@ import { ApiPurposeVersionDocument, ApiPurposeVersionState, ApiRiskAnalysisForm, - ApiRiskAnalysisFormConfig, + ApiRiskAnalysisFormRules, } from "./models.js"; export const singleAnswersToApiSingleAnswers = ( @@ -154,7 +156,7 @@ export const dataTypeToApiDataType = (type: DataType): ApiDataType => match(type) .with(dataType.single, () => "SINGLE") .with(dataType.multi, () => "MULTI") - .with(dataType.freetext, () => "FREETEXT") + .with(dataType.freeText, () => "FREETEXT") .exhaustive(); export const dependencyToApiDependency = ( @@ -188,8 +190,8 @@ export const labeledValueToApiLabeledValue = ( }); export const formConfigQuestionToApiFormConfigQuestion = ( - question: FormConfigQuestion -): ApiFormConfigQuestion => { + question: FormQuestionRules +): ApiFormQuestionRules => { const commonFields = { id: question.id, label: localizedTextToApiLocalizedText(question.label), @@ -206,8 +208,8 @@ export const formConfigQuestionToApiFormConfigQuestion = ( : undefined, }; - return match(question) - .with({ dataType: dataType.freetext }, () => commonFields) + return match(question) + .with({ dataType: dataType.freeText }, () => commonFields) .with({ dataType: dataType.single }, { dataType: dataType.multi }, (q) => ({ ...commonFields, options: q.options.map(labeledValueToApiLabeledValue), @@ -216,8 +218,8 @@ export const formConfigQuestionToApiFormConfigQuestion = ( }; export const riskAnalysisFormConfigToApiRiskAnalysisFormConfig = ( - configuration: RiskAnalysisFormConfig -): ApiRiskAnalysisFormConfig => ({ + configuration: RiskAnalysisFormRules +): ApiRiskAnalysisFormRules => ({ version: configuration.version, questions: configuration.questions.map( formConfigQuestionToApiFormConfigQuestion diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 70086887b1..a55edceffb 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -45,10 +45,10 @@ export type ApiReversePurposeSeed = z.infer< export type ApiPurposeCloneSeed = z.infer; -export type ApiFormConfigQuestion = z.infer< +export type ApiFormQuestionRules = z.infer< typeof api.schemas.FormConfigQuestionResponse >; -export type ApiRiskAnalysisFormConfig = z.infer< +export type ApiRiskAnalysisFormRules = z.infer< typeof api.schemas.RiskAnalysisFormConfigResponse >; diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index d75f946848..631b47c22f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -4,8 +4,8 @@ import { eventRepository, formatDateAndTime, logger, + riskAnalysisFormRules, riskAnalysisFormToRiskAnalysisFormToValidate, - riskAnalysisFormsMap, validateRiskAnalysis, } from "pagopa-interop-commons"; import { @@ -863,13 +863,15 @@ export function purposeServiceBuilder( assertTenantKindExists(tenant); - const kindConfig = riskAnalysisFormsMap[tenant.kind]; + const kindConfig = riskAnalysisFormRules[tenant.kind]; if (!kindConfig) { throw RiskAnalysisConfigForTenantKindNotFound(tenant.id); } - const riskAnalysisFormConfig = kindConfig[riskAnalysisVersion]; + const riskAnalysisFormConfig = kindConfig.find( + (config) => config.version === riskAnalysisVersion + ); if (!riskAnalysisFormConfig) { throw RiskAnalysisConfigVersionNotFound( From 93a89254846688ef69cffca2a8f2c8e769fc9041 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 6 May 2024 12:46:00 +0200 Subject: [PATCH 405/537] fix import --- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 631b47c22f..173c057700 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,6 +1,7 @@ import { CreateEvent, DB, + RiskAnalysisFormRules, eventRepository, formatDateAndTime, logger, @@ -35,7 +36,6 @@ import { Agreement, RiskAnalysisId, RiskAnalysis, - RiskAnalysisFormConfig, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -849,7 +849,7 @@ export function purposeServiceBuilder( eserviceId: EServiceId; riskAnalysisVersion: string; organizationId: TenantId; - }): Promise { + }): Promise { logger.info( `Retrieve version ${riskAnalysisVersion} of risk analysis configuration` ); From b4a7218b592f7aa55692477dd597e263d8e0430f Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 6 May 2024 15:49:35 +0200 Subject: [PATCH 406/537] implemented tests --- packages/commons/src/risk-analysis/index.ts | 3 +- .../src/risk-analysis/rules/PA/index.ts | 3 + .../src/risk-analysis/rules/PRIVATE/index.ts | 2 + .../commons/src/risk-analysis/rules/index.ts | 4 + .../src/model/domain/errors.ts | 11 -- .../src/routers/PurposeRouter.ts | 2 +- .../src/services/purposeService.ts | 9 +- .../src/utilities/errorMappers.ts | 7 +- .../test/purposeService.integration.test.ts | 2 + ...stGetRiskAnalysisConfigurationByVersion.ts | 142 ++++++++++++++++++ 10 files changed, 157 insertions(+), 28 deletions(-) create mode 100644 packages/commons/src/risk-analysis/rules/PA/index.ts create mode 100644 packages/commons/src/risk-analysis/rules/PRIVATE/index.ts create mode 100644 packages/commons/src/risk-analysis/rules/index.ts create mode 100644 packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts diff --git a/packages/commons/src/risk-analysis/index.ts b/packages/commons/src/risk-analysis/index.ts index c6af0acd2a..4db12e5501 100644 --- a/packages/commons/src/risk-analysis/index.ts +++ b/packages/commons/src/risk-analysis/index.ts @@ -1,5 +1,4 @@ export * from "./models.js"; export * from "./riskAnalysisErrors.js"; export * from "./riskAnalysisValidation.js"; -export * from "./rules/riskAnalysisFormRules.js"; -export * from "./rules/riskAnalysisFormRulesProvider.js"; +export * from "./rules/index.js"; diff --git a/packages/commons/src/risk-analysis/rules/PA/index.ts b/packages/commons/src/risk-analysis/rules/PA/index.ts new file mode 100644 index 0000000000..f16c998ef8 --- /dev/null +++ b/packages/commons/src/risk-analysis/rules/PA/index.ts @@ -0,0 +1,3 @@ +export * from "./1.0.js"; +export * from "./2.0.js"; +export * from "./3.0.js"; diff --git a/packages/commons/src/risk-analysis/rules/PRIVATE/index.ts b/packages/commons/src/risk-analysis/rules/PRIVATE/index.ts new file mode 100644 index 0000000000..4cd22dca63 --- /dev/null +++ b/packages/commons/src/risk-analysis/rules/PRIVATE/index.ts @@ -0,0 +1,2 @@ +export * from "./1.0.js"; +export * from "./2.0.js"; diff --git a/packages/commons/src/risk-analysis/rules/index.ts b/packages/commons/src/risk-analysis/rules/index.ts new file mode 100644 index 0000000000..509c401d37 --- /dev/null +++ b/packages/commons/src/risk-analysis/rules/index.ts @@ -0,0 +1,4 @@ +export * from "./PA/index.js"; +export * from "./PRIVATE/index.js"; +export * from "./riskAnalysisFormRulesProvider.js"; +export * from "./riskAnalysisFormRules.js"; diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 281fa2beff..b88d566ed9 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -36,7 +36,6 @@ export const errorCodes = { eserviceRiskAnalysisNotFound: "0020", purposeCannotBeCloned: "0021", riskAnalysisConfigVersionNotFound: "0022", - riskAnalysisConfigForTenantKindNotFound: "0023", }; export type ErrorCodes = keyof typeof errorCodes; @@ -257,13 +256,3 @@ export function RiskAnalysisConfigVersionNotFound( title: "Risk Analysis config version not found", }); } - -export function RiskAnalysisConfigForTenantKindNotFound( - tenantId: TenantId -): ApiError { - return new ApiError({ - detail: `Risk Analysis Configuration for tenant ${tenantId} not found`, - code: "riskAnalysisConfigForTenantKindNotFound", - title: "Risk Analysis config for tenant kind not found", - }); -} diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index f6e21e37ab..27f713a27a 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -20,7 +20,6 @@ import { } from "../model/domain/apiConverter.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../utilities/config.js"; -import { purposeServiceBuilder } from "../services/purposeServiceBuilder.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { archivePurposeVersionErrorMapper, @@ -36,6 +35,7 @@ import { suspendPurposeVersionErrorMapper, updatePurposeErrorMapper, } from "../utilities/errorMappers.js"; +import { purposeServiceBuilder } from "../services/purposeService.js"; const readModelService = readModelServiceBuilder( ReadModelRepository.init(config) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 173c057700..a19ca673df 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -56,7 +56,6 @@ import { purposeVersionNotFound, tenantNotFound, RiskAnalysisConfigVersionNotFound, - RiskAnalysisConfigForTenantKindNotFound, } from "../model/domain/errors.js"; import { toCreateEventDraftPurposeDeleted, @@ -863,13 +862,7 @@ export function purposeServiceBuilder( assertTenantKindExists(tenant); - const kindConfig = riskAnalysisFormRules[tenant.kind]; - - if (!kindConfig) { - throw RiskAnalysisConfigForTenantKindNotFound(tenant.id); - } - - const riskAnalysisFormConfig = kindConfig.find( + const riskAnalysisFormConfig = riskAnalysisFormRules[tenant.kind].find( (config) => config.version === riskAnalysisVersion ); diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index cf7a35b5e5..c8bf32f981 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -178,10 +178,5 @@ export const retrieveRiskAnalysisConfigurationByVersionErrorMapper = ( "riskAnalysisConfigVersionNotFound", () => HTTP_STATUS_NOT_FOUND ) - .with( - "tenantNotFound", - "tenantKindNotFound", - "riskAnalysisConfigForTenantKindNotFound", - () => HTTP_STATUS_BAD_REQUEST - ) + .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index ad234e85d5..3a4e01af88 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -41,6 +41,7 @@ import { testGetPurposes } from "./testGetPurposes.js"; import { testCreatePurpose } from "./testCreatePurpose.js"; import { testCreateReversePurpose } from "./testCreateReversePurpose.js"; import { testClonePurpose } from "./testClonePurpose.js"; +import { testGetRiskAnalysisConfigurationByVersion } from "./testGetRiskAnalysisConfigurationByVersion.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; @@ -108,5 +109,6 @@ describe("Integration tests", async () => { testCreatePurpose(); testCreateReversePurpose(); testClonePurpose(); + testGetRiskAnalysisConfigurationByVersion(); }); }); diff --git a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts new file mode 100644 index 0000000000..50f9e062b1 --- /dev/null +++ b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts @@ -0,0 +1,142 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + getMockEService, + getMockTenant, + randomArrayItem, + writeInReadmodel, +} from "pagopa-interop-commons-test/index.js"; +import { + EServiceId, + TenantId, + eserviceMode, + generateId, + tenantKind, + toReadModelEService, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { pa1, private1 } from "pagopa-interop-commons"; +import { + RiskAnalysisConfigVersionNotFound, + eserviceNotFound, + tenantKindNotFound, + tenantNotFound, +} from "../src/model/domain/errors.js"; +import { + eservices, + purposeService, + tenants, +} from "./purposeService.integration.test.js"; +export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< + typeof describe +> => + describe("retrieveRiskAnalysisConfigurationByVersion", async () => { + it("should retrieve risk analysis configuration by version (Eservice mode: deliver)", async () => { + const mockEservice = { ...getMockEService(), mode: eserviceMode.deliver }; + const kind = randomArrayItem(Object.values(tenantKind)); + const mockTenant = { + ...getMockTenant(), + kind, + }; + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(mockTenant, tenants); + + const result = + await purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion: "1.0", + organizationId: mockTenant.id, + }); + + const riskAnalysisFormConfig = kind === tenantKind.PA ? pa1 : private1; + + expect(result).toEqual(riskAnalysisFormConfig); + }); + it("should retrieve risk analysis configuration by version (Eservice mode: receive)", async () => { + const mockEservice = { ...getMockEService(), mode: eserviceMode.receive }; + const kind = randomArrayItem(Object.values(tenantKind)); + const mockTenant = { + ...getMockTenant(mockEservice.producerId), + kind, + }; + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(mockTenant, tenants); + + const result = + await purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion: "1.0", + organizationId: mockTenant.id, + }); + + const riskAnalysisFormConfig = kind === tenantKind.PA ? pa1 : private1; + + expect(result).toEqual(riskAnalysisFormConfig); + }); + it("should throw eserviceNotFound if the eservice doesn't exist", async () => { + const mockTenant = getMockTenant(); + const randomId = generateId(); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: randomId, + riskAnalysisVersion: "1.0", + organizationId: mockTenant.id, + }) + ).rejects.toThrowError(eserviceNotFound(randomId)); + }); + it("should throw tenantNotFound if the tenant doesn't exist", async () => { + const mockEservice = { ...getMockEService(), mode: eserviceMode.deliver }; + const randomId = generateId(); + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + + expect( + purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion: "1.0", + organizationId: randomId, + }) + ).rejects.toThrowError(tenantNotFound(randomId)); + }); + it("should throw tenantKindNotFound if the tenant kind is undefined", async () => { + const mockEservice = { ...getMockEService(), mode: eserviceMode.deliver }; + const mockTenant = { + ...getMockTenant(), + kind: undefined, + }; + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion: "1.0", + organizationId: mockTenant.id, + }) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + it("should throw RiskAnalysisConfigVersionNotFound if a config with that version doesn't exist", async () => { + const mockEservice = { ...getMockEService(), mode: eserviceMode.deliver }; + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(mockTenant, tenants); + + const wrongRiskAnalysisVersion = "0.0"; + + expect( + purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion: wrongRiskAnalysisVersion, + organizationId: mockTenant.id, + }) + ).rejects.toThrowError( + RiskAnalysisConfigVersionNotFound( + wrongRiskAnalysisVersion, + mockTenant.kind + ) + ); + }); + }); From 2e299563d1fdd05e5ecad6135a6cec6481f19b6b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 09:46:01 +0200 Subject: [PATCH 407/537] Add to do --- packages/notifier-seeder/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 4eaaf4f725..1fc0167112 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -63,6 +63,8 @@ export function processMessage(topicConfig: CatalogTopicConfig) { }; } +// to do: add purpose events + await runConsumer( config, [topicsConfig.catalogTopic], From fe31bf9f93f7613b0a08115f2ebe9fe627700890 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Tue, 7 May 2024 15:00:00 +0200 Subject: [PATCH 408/537] implemented suggestions from review --- .../test/getFormRules.unit.test.ts | 20 +++++++++++++++++++ .../risk-analysis/riskAnalysisValidation.ts | 9 +++++++++ .../commons/src/risk-analysis/rules/index.ts | 1 - .../src/services/purposeService.ts | 7 ++++--- ...stGetRiskAnalysisConfigurationByVersion.ts | 18 ++++++++--------- 5 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 packages/commons-test/test/getFormRules.unit.test.ts diff --git a/packages/commons-test/test/getFormRules.unit.test.ts b/packages/commons-test/test/getFormRules.unit.test.ts new file mode 100644 index 0000000000..3b6aa2360c --- /dev/null +++ b/packages/commons-test/test/getFormRules.unit.test.ts @@ -0,0 +1,20 @@ +import { getFormRulesByVersion, pa1, private1 } from "pagopa-interop-commons"; +import { tenantKind } from "pagopa-interop-models"; +import { describe, it, expect } from "vitest"; + +describe("Form rules retrieve", () => { + describe("getFormRulesByVersion", () => { + it.each(Object.values(tenantKind))( + "should retrieve form rules of version 1.0 for kind %s", + (kind) => { + const riskAnalysisFormConfig = kind === tenantKind.PA ? pa1 : private1; + expect(getFormRulesByVersion(kind, "1.0")).toEqual( + riskAnalysisFormConfig + ); + } + ); + it("Should not retrieve form rules for unknown version", () => { + expect(getFormRulesByVersion(tenantKind.PA, "0.0")).toBeUndefined(); + }); + }); +}); diff --git a/packages/commons/src/risk-analysis/riskAnalysisValidation.ts b/packages/commons/src/risk-analysis/riskAnalysisValidation.ts index 004eb2760a..7cbc139ef0 100644 --- a/packages/commons/src/risk-analysis/riskAnalysisValidation.ts +++ b/packages/commons/src/risk-analysis/riskAnalysisValidation.ts @@ -116,6 +116,15 @@ function getLatestVersionFormRules( } } +export function getFormRulesByVersion( + tenantKind: TenantKind, + version: string +): RiskAnalysisFormRules | undefined { + return riskAnalysisFormRules[tenantKind].find( + (config) => config.version === version + ); +} + function questionRulesDepsToValidationRuleDeps( dependencies: FormQuestionRules["dependencies"] ): ValidationRuleDependency[] { diff --git a/packages/commons/src/risk-analysis/rules/index.ts b/packages/commons/src/risk-analysis/rules/index.ts index 509c401d37..946b709b46 100644 --- a/packages/commons/src/risk-analysis/rules/index.ts +++ b/packages/commons/src/risk-analysis/rules/index.ts @@ -1,4 +1,3 @@ export * from "./PA/index.js"; export * from "./PRIVATE/index.js"; -export * from "./riskAnalysisFormRulesProvider.js"; export * from "./riskAnalysisFormRules.js"; diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a19ca673df..1077234454 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -4,8 +4,8 @@ import { RiskAnalysisFormRules, eventRepository, formatDateAndTime, + getFormRulesByVersion, logger, - riskAnalysisFormRules, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, } from "pagopa-interop-commons"; @@ -862,8 +862,9 @@ export function purposeServiceBuilder( assertTenantKindExists(tenant); - const riskAnalysisFormConfig = riskAnalysisFormRules[tenant.kind].find( - (config) => config.version === riskAnalysisVersion + const riskAnalysisFormConfig = getFormRulesByVersion( + tenant.kind, + riskAnalysisVersion ); if (!riskAnalysisFormConfig) { diff --git a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts index 50f9e062b1..1df0d832e9 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts @@ -14,7 +14,7 @@ import { toReadModelEService, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; -import { pa1, private1 } from "pagopa-interop-commons"; +import { getFormRulesByVersion } from "pagopa-interop-commons"; import { RiskAnalysisConfigVersionNotFound, eserviceNotFound, @@ -40,16 +40,16 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< await writeInReadmodel(toReadModelEService(mockEservice), eservices); await writeInReadmodel(mockTenant, tenants); + const riskAnalysisVersion = "1.0"; + const result = await purposeService.retrieveRiskAnalysisConfigurationByVersion({ eserviceId: mockEservice.id, - riskAnalysisVersion: "1.0", + riskAnalysisVersion, organizationId: mockTenant.id, }); - const riskAnalysisFormConfig = kind === tenantKind.PA ? pa1 : private1; - - expect(result).toEqual(riskAnalysisFormConfig); + expect(result).toEqual(getFormRulesByVersion(kind, riskAnalysisVersion)); }); it("should retrieve risk analysis configuration by version (Eservice mode: receive)", async () => { const mockEservice = { ...getMockEService(), mode: eserviceMode.receive }; @@ -61,16 +61,16 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< await writeInReadmodel(toReadModelEService(mockEservice), eservices); await writeInReadmodel(mockTenant, tenants); + const riskAnalysisVersion = "1.0"; + const result = await purposeService.retrieveRiskAnalysisConfigurationByVersion({ eserviceId: mockEservice.id, - riskAnalysisVersion: "1.0", + riskAnalysisVersion, organizationId: mockTenant.id, }); - const riskAnalysisFormConfig = kind === tenantKind.PA ? pa1 : private1; - - expect(result).toEqual(riskAnalysisFormConfig); + expect(result).toEqual(getFormRulesByVersion(kind, riskAnalysisVersion)); }); it("should throw eserviceNotFound if the eservice doesn't exist", async () => { const mockTenant = getMockTenant(); From 28cedaa1118b5e2a2b0542e6cbf0de3dcf03fe78 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 15:23:45 +0200 Subject: [PATCH 409/537] Draft --- packages/commons/src/kafka/kafka.ts | 6 +- packages/notifier-seeder-purpose/.env | 9 ++ packages/notifier-seeder-purpose/Dockerfile | 45 ++++++++++ .../notifier-seeder-purpose/aws.config.local | 4 + .../elasticmq.local.conf | 6 ++ packages/notifier-seeder-purpose/package.json | 52 ++++++++++++ .../src/config/notificationConfig.ts | 13 +++ packages/notifier-seeder-purpose/src/index.ts | 76 +++++++++++++++++ .../src/models/purposeEventNotification.ts | 83 +++++++++++++++++++ .../purposeEventNotificationConverter.ts | 61 ++++++++++++++ .../models/purposeEventNotificationMappers.ts | 56 +++++++++++++ .../models/purposeEventNotificationMessage.ts | 58 +++++++++++++ .../src/notifierErrors.ts | 12 +++ .../src/queue-manager/queueManager.ts | 75 +++++++++++++++++ .../src/queue-manager/queueManagerErrors.ts | 41 +++++++++ .../src/queue-manager/queueMessage.ts | 12 +++ .../test/tsconfig.json | 4 + .../notifier-seeder-purpose/test/utils.ts | 14 ++++ .../notifier-seeder-purpose/tsconfig.json | 7 ++ .../notifier-seeder-purpose/vitest.config.ts | 9 ++ packages/notifier-seeder/src/index.ts | 2 - pnpm-lock.yaml | 73 ++++++++++++++++ 22 files changed, 715 insertions(+), 3 deletions(-) create mode 100644 packages/notifier-seeder-purpose/.env create mode 100644 packages/notifier-seeder-purpose/Dockerfile create mode 100644 packages/notifier-seeder-purpose/aws.config.local create mode 100644 packages/notifier-seeder-purpose/elasticmq.local.conf create mode 100644 packages/notifier-seeder-purpose/package.json create mode 100644 packages/notifier-seeder-purpose/src/config/notificationConfig.ts create mode 100644 packages/notifier-seeder-purpose/src/index.ts create mode 100644 packages/notifier-seeder-purpose/src/models/purposeEventNotification.ts create mode 100644 packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts create mode 100644 packages/notifier-seeder-purpose/src/models/purposeEventNotificationMappers.ts create mode 100644 packages/notifier-seeder-purpose/src/models/purposeEventNotificationMessage.ts create mode 100644 packages/notifier-seeder-purpose/src/notifierErrors.ts create mode 100644 packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts create mode 100644 packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts create mode 100644 packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts create mode 100644 packages/notifier-seeder-purpose/test/tsconfig.json create mode 100644 packages/notifier-seeder-purpose/test/utils.ts create mode 100644 packages/notifier-seeder-purpose/tsconfig.json create mode 100644 packages/notifier-seeder-purpose/vitest.config.ts diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index 94f195c0f6..2d51c8eca7 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { z } from "zod"; import { EachMessagePayload, KafkaMessage } from "kafkajs"; -import { EServiceEvent, Message } from "pagopa-interop-models"; +import { EServiceEvent, Message, PurposeEvent } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { KafkaTopicConfig } from "../config/kafkaTopicConfig.js"; @@ -47,6 +47,10 @@ export const messageDecoderSupplier = ( () => (message: KafkaMessage) => decodeKafkaMessage(message, EServiceEvent) ) + .with( + { purposeTopic: P.string }, + () => (message: KafkaMessage) => decodeKafkaMessage(message, PurposeEvent) + ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); }); diff --git a/packages/notifier-seeder-purpose/.env b/packages/notifier-seeder-purpose/.env new file mode 100644 index 0000000000..fb43871763 --- /dev/null +++ b/packages/notifier-seeder-purpose/.env @@ -0,0 +1,9 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="notifier-seeder-client" +KAFKA_GROUP_ID="notifier-seeder--group-local" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH=true +PURPOSE_TOPIC="event-store.purpose.events" +AWS_REGION="eu-central-1" +NOTIFICATION_QUEUE_URL="http://localhost:9324/000000000000/sqsLocalQueue.fifo" \ No newline at end of file diff --git a/packages/notifier-seeder-purpose/Dockerfile b/packages/notifier-seeder-purpose/Dockerfile new file mode 100644 index 0000000000..cfe0cfa405 --- /dev/null +++ b/packages/notifier-seeder-purpose/Dockerfile @@ -0,0 +1,45 @@ +FROM node:18.17.1-slim as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ + +COPY ./packages/notifier-seeder/package.json /app/packages/notifier-seeder/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ + +COPY ./packages/notifier-seeder /app/packages/notifier-seeder +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/notifier-seeder/node_modules \ + package*.json packages/notifier-seeder/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/notifier-seeder/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:18.17.1-slim as final + +COPY --from=build /out /app + +WORKDIR /app/packages/notifier-seeder +EXPOSE 3000 + +CMD ["node", "."] \ No newline at end of file diff --git a/packages/notifier-seeder-purpose/aws.config.local b/packages/notifier-seeder-purpose/aws.config.local new file mode 100644 index 0000000000..c2cb4f4115 --- /dev/null +++ b/packages/notifier-seeder-purpose/aws.config.local @@ -0,0 +1,4 @@ +[default] +aws_access_key_id=test-aws-key +aws_secret_access_key=test-aws-secret +region=eu-central-1 diff --git a/packages/notifier-seeder-purpose/elasticmq.local.conf b/packages/notifier-seeder-purpose/elasticmq.local.conf new file mode 100644 index 0000000000..eee8e8661a --- /dev/null +++ b/packages/notifier-seeder-purpose/elasticmq.local.conf @@ -0,0 +1,6 @@ +queues { + sqsLocalQueue { + fifo = true + contentBasedDeduplication = true + } +} diff --git a/packages/notifier-seeder-purpose/package.json b/packages/notifier-seeder-purpose/package.json new file mode 100644 index 0000000000..42ad02e0fd --- /dev/null +++ b/packages/notifier-seeder-purpose/package.json @@ -0,0 +1,52 @@ +{ + "name": "pagopa-interop-notifier-seeder-purpose", + "private": true, + "version": "1.0.0", + "description": "Pagopa Interop notifier seeder", + "main": "dist", + "type": "module", + "exports": { + ".": "./dist/index.js" + }, + "scripts": { + "test": "vitest", + "test:it": "vitest integration", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "node --watch --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", + "build": "tsc" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/dotenv-flow": "3.2.0", + "@types/node": "20.3.1", + "prettier": "2.8.8", + "ts-node": "10.9.2", + "typescript": "5.1.3", + "dotenv-flow": "3.2.0", + "kafkajs": "2.2.4", + "@types/uuid": "9.0.8", + "mkdirp": "3.0.1", + "tsc-esm-fix": "2.20.26", + "testcontainers": "10.2.2", + "vitest": "0.33.0" + }, + "dependencies": { + "@protobuf-ts/plugin": "2.9.4", + "@protobuf-ts/protoc": "2.9.4", + "@protobuf-ts/runtime": "2.9.4", + "@aws-sdk/client-sqs": "3.529.1", + "dotenv-flow": "3.2.0", + "kafka-iam-auth": "workspace:*", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:^", + "ts-pattern": "5.0.6", + "uuid": "9.0.1", + "zod": "3.21.4" + } +} diff --git a/packages/notifier-seeder-purpose/src/config/notificationConfig.ts b/packages/notifier-seeder-purpose/src/config/notificationConfig.ts new file mode 100644 index 0000000000..c657cb2846 --- /dev/null +++ b/packages/notifier-seeder-purpose/src/config/notificationConfig.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; + +export const NotificationConfig = z + .object({ + NOTIFICATION_QUEUE_URL: z.string(), + }) + .transform((c) => ({ + queueUrl: c.NOTIFICATION_QUEUE_URL, + })); +export type NotificationConfig = z.infer; + +export const notificationConfig = (): NotificationConfig => + NotificationConfig.parse(process.env); diff --git a/packages/notifier-seeder-purpose/src/index.ts b/packages/notifier-seeder-purpose/src/index.ts new file mode 100644 index 0000000000..8743d65040 --- /dev/null +++ b/packages/notifier-seeder-purpose/src/index.ts @@ -0,0 +1,76 @@ +/* eslint-disable functional/immutable-data */ +import { runConsumer } from "kafka-iam-auth"; +import { EachMessagePayload } from "kafkajs"; +import { + PurposeTopicConfig, + kafkaConsumerConfig, + logger, + loggerConfig, + messageDecoderSupplier, + purposeTopicConfig, + runWithContext, +} from "pagopa-interop-commons"; +import { initQueueManager } from "./queue-manager/queueManager.js"; +import { notificationConfig } from "./config/notificationConfig.js"; +import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; +import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; + +const config = kafkaConsumerConfig(); +const topicsConfig = purposeTopicConfig(); +const logConfig = loggerConfig(); +const queueConfig = notificationConfig(); +const queueManager = initQueueManager({ + queueUrl: queueConfig.queueUrl, + messageGroupId: "message_group_all_notification", + logLevel: logConfig.logLevel, +}); + +export function processMessage(topicConfig: PurposeTopicConfig) { + return async (kafkaMessage: EachMessagePayload): Promise => { + const messageDecoder = messageDecoderSupplier( + topicConfig, + kafkaMessage.topic + ); + + const decodedMessage = messageDecoder(kafkaMessage.message); + + await runWithContext( + { + messageData: { + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + }, + correlationId: decodedMessage.correlation_id, + }, + async () => { + if (decodedMessage.event_version !== 2) { + logger.info( + `Event with version ${decodedMessage.event_version} skipped` + ); + return; + } + + const purposeNotificationV1Event = + toPurposeEventNotification(decodedMessage); + const message = buildPurposeMessage( + decodedMessage, + purposeNotificationV1Event + ); + await queueManager.send(message); + + logger.info( + `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` + ); + } + ); + }; +} + +// to do: add purpose events + +await runConsumer( + config, + [topicsConfig.purposeTopic], + processMessage(topicsConfig) +); diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotification.ts b/packages/notifier-seeder-purpose/src/models/purposeEventNotification.ts new file mode 100644 index 0000000000..b43c3f1ba9 --- /dev/null +++ b/packages/notifier-seeder-purpose/src/models/purposeEventNotification.ts @@ -0,0 +1,83 @@ +import { + PurposeV1, + PurposeVersionDocument, + PurposeVersionV1, +} from "pagopa-interop-models"; + +export type PurposeVersionDocumentV1Notification = Omit< + PurposeVersionDocument, + "createdAt" +> & { + createdAt: string; +}; +export type PurposeVersionV1Notification = Omit< + PurposeVersionV1, + | "state" + | "expectedApprovalDate" + | "riskAnalysis" + | "createdAt" + | "updatedAt" + | "firstActivationAt" + | "suspendedAt" +> & { + state: string; + expectedApprovalDate?: string; + riskAnalysis?: PurposeVersionDocumentV1Notification; + createdAt: string; + updatedAt?: string; + firstActivationAt?: string; + suspendedAt?: string; +}; + +export type PurposeV1Notification = Omit< + PurposeV1, + "versions" | "createdAt" | "updatedAt" +> & { + versions: PurposeVersionV1Notification[]; + createdAt: string; + updatedAt?: string; +}; + +// PurposeCreatedV1 +// PurposeUpdatedV1 +// PurposeVersionCreatedV1 +// PurposeVersionActivatedV1 +// PurposeVersionSuspendedV1 +// PurposeVersionArchivedV1 +// PurposeVersionWaitedForApprovalV1 +export type PurposeCreatedNotification = { + purpose: PurposeV1Notification; +}; + +/* +unused +// PurposeVersionCreatedV1 +// PurposeVersionUpdatedV1 +export type PurposeIdAndVersiondNotification = { + purposeId: string; + version: PurposeVersionV1Notification; +}; +*/ + +// PurposeVersionRejectedV1 +export type PurposeAndVersionIdNotification = { + purpose: PurposeV1Notification; + versionId: string; +}; + +// PurposeDeletedV1 +export type PurposeIdNotification = { + purposeId: string; +}; + +// PurposeVersionDeletedV1 +export type PurposeIdAndVersionIdNotification = { + purposeId: string; + versionId: string; +}; + +export type PurposeEventNotification = + | PurposeCreatedNotification + | PurposeAndVersionIdNotification + | PurposeIdNotification + | PurposeIdAndVersionIdNotification; diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts new file mode 100644 index 0000000000..1c3aa8324c --- /dev/null +++ b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts @@ -0,0 +1,61 @@ +import { + PurposeEventEnvelopeV2, + fromPurposeV2, + missingKafkaMessageDataError, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { + PurposeEventNotification, + PurposeV1Notification, +} from "./purposeEventNotification.js"; +import { toPurposeV1Notification } from "./purposeEventNotificationMappers.js"; + +const getPurpose = (event: PurposeEventEnvelopeV2): PurposeV1Notification => { + if (!event.data.purpose) { + throw missingKafkaMessageDataError("purpose", event.type); + } + const purpose = fromPurposeV2(event.data.purpose); + return toPurposeV1Notification(purpose); +}; + +export const toPurposeEventNotification = ( + event: PurposeEventEnvelopeV2 +): PurposeEventNotification => + match(event) + .with( + { type: "PurposeAdded" }, + { type: "DraftPurposeUpdated" }, + { type: "PurposeWaitingForApproval" }, + { type: "PurposeActivated" }, + { type: "NewPurposeVersionActivated" }, + { type: "PurposeVersionActivated" }, + { type: "PurposeVersionUnsuspendedByProducer" }, + { type: "PurposeVersionUnsuspendedByConsumer" }, + { type: "PurposeVersionSuspendedByProducer" }, + { type: "PurposeVersionSuspendedByConsumer" }, + { type: "PurposeArchived" }, + { type: "PurposeCloned" }, + { type: "NewPurposeVersionWaitingForApproval" }, + { type: "PurposeVersionOverQuotaUnsuspended" }, + (event) => ({ + purpose: getPurpose(event), + }) + ) + .with({ type: "PurposeVersionRejected" }, (event) => ({ + purpose: getPurpose(event), + versionId: event.data.versionId, + })) + .with( + { type: "DraftPurposeDeleted" }, + { type: "WaitingForApprovalPurposeDeleted" }, + (event) => ({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + purposeId: event.data.purpose!.id, + }) + ) + .with({ type: "WaitingForApprovalPurposeVersionDeleted" }, (event) => ({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + purposeId: event.data.purpose!.id, + versionId: event.data.versionId, + })) + .exhaustive(); diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMappers.ts b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMappers.ts new file mode 100644 index 0000000000..e93dd0a944 --- /dev/null +++ b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMappers.ts @@ -0,0 +1,56 @@ +import { match } from "ts-pattern"; +import { + Purpose, + PurposeVersion, + PurposeVersionDocument, + PurposeVersionState, + purposeVersionState, +} from "pagopa-interop-models"; +import { + PurposeV1Notification, + PurposeVersionDocumentV1Notification, + PurposeVersionV1Notification, +} from "./purposeEventNotification.js"; + +export const toPurposeVersionStateV1Notification = ( + input: PurposeVersionState +): string => + match(input) + .with(purposeVersionState.draft, () => "Draft") + .with(purposeVersionState.active, () => "Active") + .with(purposeVersionState.suspended, () => "Suspended") + .with(purposeVersionState.archived, () => "Archived") + .with(purposeVersionState.waitingForApproval, () => "Waiting for approval") + .with(purposeVersionState.rejected, () => "Rejected") + .exhaustive(); + +export const toPurposeVersionDocumentV1Notification = ( + input: PurposeVersionDocument +): PurposeVersionDocumentV1Notification => ({ + ...input, + createdAt: input.createdAt.toISOString(), +}); + +export const toPurposeVersionV1Notification = ( + input: PurposeVersion +): PurposeVersionV1Notification => ({ + ...input, + state: toPurposeVersionStateV1Notification(input.state), + expectedApprovalDate: input.expectedApprovalDate?.toISOString(), + createdAt: input.createdAt.toISOString(), + updatedAt: input.updatedAt?.toISOString(), + firstActivationAt: input.firstActivationAt?.toISOString(), + suspendedAt: input.suspendedAt?.toISOString(), + riskAnalysis: input.riskAnalysis + ? toPurposeVersionDocumentV1Notification(input.riskAnalysis) + : undefined, +}); + +export const toPurposeV1Notification = ( + input: Purpose +): PurposeV1Notification => ({ + ...input, + versions: input.versions.map(toPurposeVersionV1Notification), + createdAt: input.createdAt.toISOString(), + updatedAt: input.updatedAt?.toISOString(), +}); diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMessage.ts b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMessage.ts new file mode 100644 index 0000000000..e40685bd91 --- /dev/null +++ b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMessage.ts @@ -0,0 +1,58 @@ +import { PurposeEventEnvelopeV2 } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { v4 as uuidv4 } from "uuid"; +import { QueueMessage } from "../queue-manager/queueMessage.js"; +import { PurposeEventNotification } from "./purposeEventNotification.js"; + +export const eventV2TypeMapper = ( + eventType: PurposeEventEnvelopeV2["type"] +): string => + match(eventType) + .with("PurposeAdded", "PurposeCloned", () => "purpose_created") + .with("DraftPurposeUpdated", () => "purpose_updated") + .with( + "PurposeWaitingForApproval", + "NewPurposeVersionWaitingForApproval", + "PurposeVersionOverQuotaUnsuspended", + () => "purpose_version_waited_for_approval" + ) + .with( + "PurposeActivated", + "NewPurposeVersionActivated", + "PurposeVersionActivated", + () => "purpose_version_activated" + ) + .with( + "DraftPurposeDeleted", + "WaitingForApprovalPurposeDeleted", + () => "purpose_deleted" + ) + .with( + "PurposeVersionUnsuspendedByProducer", + "PurposeVersionUnsuspendedByConsumer", + () => "purpose_version_activated" + ) + .with( + "PurposeVersionSuspendedByProducer", + "PurposeVersionSuspendedByConsumer", + () => "purpose_version_suspended" + ) + .with("PurposeArchived", () => "purpose_version_archived") + .with( + "WaitingForApprovalPurposeVersionDeleted", + () => "purpose_version_deleted" + ) + .with("PurposeVersionRejected", () => "purpose_version_rejected") + .exhaustive(); + +export const buildPurposeMessage = ( + event: PurposeEventEnvelopeV2, + purposeEvent: PurposeEventNotification +): QueueMessage => ({ + messageUUID: uuidv4(), + eventJournalPersistenceId: event.stream_id, + eventJournalSequenceNumber: event.version, + eventTimestamp: Number(event.log_date), + kind: eventV2TypeMapper(event.type), + payload: purposeEvent, +}); diff --git a/packages/notifier-seeder-purpose/src/notifierErrors.ts b/packages/notifier-seeder-purpose/src/notifierErrors.ts new file mode 100644 index 0000000000..087f5e058f --- /dev/null +++ b/packages/notifier-seeder-purpose/src/notifierErrors.ts @@ -0,0 +1,12 @@ +import { InternalError } from "pagopa-interop-models"; + +type NotifierErrorCode = "eventV1ConversionError"; + +export function eventV1ConversionError( + message: string +): InternalError { + return new InternalError({ + code: "eventV1ConversionError", + detail: `Error during event V1 conversion with message: ${message}`, + }); +} diff --git a/packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts b/packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts new file mode 100644 index 0000000000..172fe174c8 --- /dev/null +++ b/packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts @@ -0,0 +1,75 @@ +import { + ReceiveMessageCommand, + SQSClient, + SendMessageCommand, +} from "@aws-sdk/client-sqs"; +import { LoggerConfig, logger } from "pagopa-interop-commons"; +import { + queueManagerReceiveError, + queueManagerSendError, +} from "./queueManagerErrors.js"; +import { QueueMessage } from "./queueMessage.js"; + +export type QueueManager = { + send: (message: QueueMessage) => Promise; + receiveLast: (msgsToReceive?: number) => Promise; +}; + +export function initQueueManager( + config: { queueUrl: string; messageGroupId: string } & LoggerConfig +): QueueManager { + const client = new SQSClient({ + logger: config.logLevel === "debug" ? console : undefined, + }); + + return { + send: async (message: QueueMessage): Promise => { + logger.debug( + `Sending message ${message.messageUUID} to queue ${config.queueUrl}` + ); + try { + const response = await client.send( + new SendMessageCommand({ + QueueUrl: config.queueUrl, + MessageBody: JSON.stringify(message, (_, v) => + typeof v === "bigint" ? v.toString() : v + ), + MessageGroupId: config.messageGroupId, + MessageDeduplicationId: `${message.eventJournalPersistenceId}_${message.eventJournalSequenceNumber}`, + }) + ); + if (!response.MessageId) { + throw new Error("Unexpected empty response from SQS"); + } + return response.MessageId; + } catch (error) { + throw queueManagerSendError(config.queueUrl, error); + } + }, + receiveLast: async (msgsToReceive: number = 1): Promise => { + logger.debug( + `Receiving last ${msgsToReceive} messages from queue ${config.queueUrl}` + ); + try { + const response = await client.send( + new ReceiveMessageCommand({ + QueueUrl: config.queueUrl, + MaxNumberOfMessages: msgsToReceive, + }) + ); + + if (!response.Messages) { + throw new Error("Unexpected empty response from SQS"); + } + + return ( + response.Messages?.map((message) => message.Body) + .filter((body): body is string => body !== undefined) + .map((body) => QueueMessage.parse(JSON.parse(body))) ?? undefined + ); + } catch (error) { + throw queueManagerReceiveError(config.queueUrl, error); + } + }, + }; +} diff --git a/packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts b/packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts new file mode 100644 index 0000000000..4c96d0ed9c --- /dev/null +++ b/packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts @@ -0,0 +1,41 @@ +import { InternalError, parseErrorMessage } from "pagopa-interop-models"; + +type QueueManagerErrorCode = + | "queueManagerSendError" + | "queueManagerReceiveError"; + +export class QueueManagerError extends InternalError { + constructor({ + code, + detail, + }: { + code: QueueManagerErrorCode; + detail: string; + }) { + super({ code, detail }); + } +} + +export function queueManagerSendError( + queueUrl: string, + error: unknown +): QueueManagerError { + return new QueueManagerError({ + code: "queueManagerSendError", + detail: `Error sending message to queue ${queueUrl}: ${parseErrorMessage( + error + )}`, + }); +} + +export function queueManagerReceiveError( + queueUrl: string, + error: unknown +): QueueManagerError { + return new QueueManagerError({ + code: "queueManagerReceiveError", + detail: `Error receiving message from queue ${queueUrl}: ${parseErrorMessage( + error + )}`, + }); +} diff --git a/packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts b/packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts new file mode 100644 index 0000000000..95c3eb7eab --- /dev/null +++ b/packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export const QueueMessage = z.object({ + messageUUID: z.string().uuid(), + eventJournalPersistenceId: z.string(), + eventJournalSequenceNumber: z.number(), + eventTimestamp: z.number(), + kind: z.string(), + payload: z.unknown(), +}); + +export type QueueMessage = z.infer; diff --git a/packages/notifier-seeder-purpose/test/tsconfig.json b/packages/notifier-seeder-purpose/test/tsconfig.json new file mode 100644 index 0000000000..f85162abb0 --- /dev/null +++ b/packages/notifier-seeder-purpose/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."], +} diff --git a/packages/notifier-seeder-purpose/test/utils.ts b/packages/notifier-seeder-purpose/test/utils.ts new file mode 100644 index 0000000000..75c4afc1e6 --- /dev/null +++ b/packages/notifier-seeder-purpose/test/utils.ts @@ -0,0 +1,14 @@ +import { GenericContainer } from "testcontainers"; + +export const TEST_ELASTIC_MQ_IMAGE = "softwaremill/elasticmq-native:1.5.7"; +export const TEST_ELASTIC_MQ_PORT = 9324; + +export const elasticMQContainer = (): GenericContainer => + new GenericContainer(TEST_ELASTIC_MQ_IMAGE) + .withCopyFilesToContainer([ + { + source: "elasticmq.local.conf", + target: "/opt/elasticmq.conf", + }, + ]) + .withExposedPorts(TEST_ELASTIC_MQ_PORT); diff --git a/packages/notifier-seeder-purpose/tsconfig.json b/packages/notifier-seeder-purpose/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/notifier-seeder-purpose/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/notifier-seeder-purpose/vitest.config.ts b/packages/notifier-seeder-purpose/vitest.config.ts new file mode 100644 index 0000000000..498a780952 --- /dev/null +++ b/packages/notifier-seeder-purpose/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + setupFiles: ["dotenv/config"], + testTimeout: 60000, + hookTimeout: 60000, + }, +}); diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 1fc0167112..4eaaf4f725 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -63,8 +63,6 @@ export function processMessage(topicConfig: CatalogTopicConfig) { }; } -// to do: add purpose events - await runConsumer( config, [topicsConfig.catalogTopic], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8f4ae21cf..cc5c0b2ce1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -879,6 +879,79 @@ importers: specifier: 0.33.0 version: 0.33.0 + packages/notifier-seeder-purpose: + dependencies: + '@aws-sdk/client-sqs': + specifier: 3.529.1 + version: 3.529.1 + '@protobuf-ts/plugin': + specifier: 2.9.4 + version: 2.9.4 + '@protobuf-ts/protoc': + specifier: 2.9.4 + version: 2.9.4 + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + dotenv-flow: + specifier: 3.2.0 + version: 3.2.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:^ + version: link:../models + ts-pattern: + specifier: 5.0.6 + version: 5.0.6 + uuid: + specifier: 9.0.1 + version: 9.0.1 + zod: + specifier: 3.21.4 + version: 3.21.4 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(typescript@5.1.3) + '@types/dotenv-flow': + specifier: 3.2.0 + version: 3.2.0 + '@types/node': + specifier: 20.3.1 + version: 20.3.1 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + kafkajs: + specifier: 2.2.4 + version: 2.2.4 + mkdirp: + specifier: 3.0.1 + version: 3.0.1 + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.2.2 + version: 10.2.2 + ts-node: + specifier: 10.9.2 + version: 10.9.2(@types/node@20.3.1)(typescript@5.1.3) + tsc-esm-fix: + specifier: 2.20.26 + version: 2.20.26 + typescript: + specifier: 5.1.3 + version: 5.1.3 + vitest: + specifier: 0.33.0 + version: 0.33.0 + packages/purpose-process: dependencies: '@zodios/core': From ef773323c5f49d78dd67a5aeed6d31c8d43c0697 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 15:43:53 +0200 Subject: [PATCH 410/537] Improvement --- .../src/models/purposeEventNotificationConverter.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts index 1c3aa8324c..a382e75156 100644 --- a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts +++ b/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts @@ -49,13 +49,11 @@ export const toPurposeEventNotification = ( { type: "DraftPurposeDeleted" }, { type: "WaitingForApprovalPurposeDeleted" }, (event) => ({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - purposeId: event.data.purpose!.id, + purposeId: getPurpose(event).id, }) ) .with({ type: "WaitingForApprovalPurposeVersionDeleted" }, (event) => ({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - purposeId: event.data.purpose!.id, + purposeId: getPurpose(event).id, versionId: event.data.versionId, })) .exhaustive(); From 00469be2464bea184b82fca51ca4a1546ef1ed5e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:08:36 +0200 Subject: [PATCH 411/537] Refactor --- packages/notifier-seeder-purpose/.env | 9 --- packages/notifier-seeder-purpose/Dockerfile | 45 ----------- .../notifier-seeder-purpose/aws.config.local | 4 - .../elasticmq.local.conf | 6 -- packages/notifier-seeder-purpose/package.json | 52 ------------- .../src/config/notificationConfig.ts | 13 ---- packages/notifier-seeder-purpose/src/index.ts | 76 ------------------- .../src/notifierErrors.ts | 12 --- .../src/queue-manager/queueManager.ts | 75 ------------------ .../src/queue-manager/queueManagerErrors.ts | 41 ---------- .../src/queue-manager/queueMessage.ts | 12 --- .../test/tsconfig.json | 4 - .../notifier-seeder-purpose/test/utils.ts | 14 ---- .../notifier-seeder-purpose/tsconfig.json | 7 -- .../notifier-seeder-purpose/vitest.config.ts | 9 --- packages/notifier-seeder/src/index.ts | 45 ++++++++--- .../src/models/purposeEventNotification.ts | 0 .../purposeEventNotificationConverter.ts | 0 .../models/purposeEventNotificationMappers.ts | 0 .../models/purposeEventNotificationMessage.ts | 0 20 files changed, 36 insertions(+), 388 deletions(-) delete mode 100644 packages/notifier-seeder-purpose/.env delete mode 100644 packages/notifier-seeder-purpose/Dockerfile delete mode 100644 packages/notifier-seeder-purpose/aws.config.local delete mode 100644 packages/notifier-seeder-purpose/elasticmq.local.conf delete mode 100644 packages/notifier-seeder-purpose/package.json delete mode 100644 packages/notifier-seeder-purpose/src/config/notificationConfig.ts delete mode 100644 packages/notifier-seeder-purpose/src/index.ts delete mode 100644 packages/notifier-seeder-purpose/src/notifierErrors.ts delete mode 100644 packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts delete mode 100644 packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts delete mode 100644 packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts delete mode 100644 packages/notifier-seeder-purpose/test/tsconfig.json delete mode 100644 packages/notifier-seeder-purpose/test/utils.ts delete mode 100644 packages/notifier-seeder-purpose/tsconfig.json delete mode 100644 packages/notifier-seeder-purpose/vitest.config.ts rename packages/{notifier-seeder-purpose => notifier-seeder}/src/models/purposeEventNotification.ts (100%) rename packages/{notifier-seeder-purpose => notifier-seeder}/src/models/purposeEventNotificationConverter.ts (100%) rename packages/{notifier-seeder-purpose => notifier-seeder}/src/models/purposeEventNotificationMappers.ts (100%) rename packages/{notifier-seeder-purpose => notifier-seeder}/src/models/purposeEventNotificationMessage.ts (100%) diff --git a/packages/notifier-seeder-purpose/.env b/packages/notifier-seeder-purpose/.env deleted file mode 100644 index fb43871763..0000000000 --- a/packages/notifier-seeder-purpose/.env +++ /dev/null @@ -1,9 +0,0 @@ -LOG_LEVEL=info - -KAFKA_CLIENT_ID="notifier-seeder-client" -KAFKA_GROUP_ID="notifier-seeder--group-local" -KAFKA_BROKERS="localhost:9092" -KAFKA_DISABLE_AWS_IAM_AUTH=true -PURPOSE_TOPIC="event-store.purpose.events" -AWS_REGION="eu-central-1" -NOTIFICATION_QUEUE_URL="http://localhost:9324/000000000000/sqsLocalQueue.fifo" \ No newline at end of file diff --git a/packages/notifier-seeder-purpose/Dockerfile b/packages/notifier-seeder-purpose/Dockerfile deleted file mode 100644 index cfe0cfa405..0000000000 --- a/packages/notifier-seeder-purpose/Dockerfile +++ /dev/null @@ -1,45 +0,0 @@ -FROM node:18.17.1-slim as build - -RUN corepack enable - -WORKDIR /app -COPY package.json /app/ -COPY pnpm-lock.yaml /app/ -COPY pnpm-workspace.yaml /app/ - -COPY ./packages/notifier-seeder/package.json /app/packages/notifier-seeder/package.json -COPY ./packages/commons/package.json /app/packages/commons/package.json -COPY ./packages/models/package.json /app/packages/models/package.json -COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json - -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile - -COPY tsconfig.json /app/ -COPY turbo.json /app/ - -COPY ./packages/notifier-seeder /app/packages/notifier-seeder -COPY ./packages/commons /app/packages/commons -COPY ./packages/models /app/packages/models -COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth - -RUN pnpm build && \ - rm -rf /app/node_modules/.modules.yaml && \ - rm -rf /app/node_modules/.cache && \ - mkdir /out && \ - cp -a --parents -t /out \ - node_modules packages/notifier-seeder/node_modules \ - package*.json packages/notifier-seeder/package*.json \ - packages/commons \ - packages/models \ - packages/kafka-iam-auth \ - packages/notifier-seeder/dist && \ - find /out -exec touch -h --date=@0 {} \; - -FROM node:18.17.1-slim as final - -COPY --from=build /out /app - -WORKDIR /app/packages/notifier-seeder -EXPOSE 3000 - -CMD ["node", "."] \ No newline at end of file diff --git a/packages/notifier-seeder-purpose/aws.config.local b/packages/notifier-seeder-purpose/aws.config.local deleted file mode 100644 index c2cb4f4115..0000000000 --- a/packages/notifier-seeder-purpose/aws.config.local +++ /dev/null @@ -1,4 +0,0 @@ -[default] -aws_access_key_id=test-aws-key -aws_secret_access_key=test-aws-secret -region=eu-central-1 diff --git a/packages/notifier-seeder-purpose/elasticmq.local.conf b/packages/notifier-seeder-purpose/elasticmq.local.conf deleted file mode 100644 index eee8e8661a..0000000000 --- a/packages/notifier-seeder-purpose/elasticmq.local.conf +++ /dev/null @@ -1,6 +0,0 @@ -queues { - sqsLocalQueue { - fifo = true - contentBasedDeduplication = true - } -} diff --git a/packages/notifier-seeder-purpose/package.json b/packages/notifier-seeder-purpose/package.json deleted file mode 100644 index 42ad02e0fd..0000000000 --- a/packages/notifier-seeder-purpose/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "pagopa-interop-notifier-seeder-purpose", - "private": true, - "version": "1.0.0", - "description": "Pagopa Interop notifier seeder", - "main": "dist", - "type": "module", - "exports": { - ".": "./dist/index.js" - }, - "scripts": { - "test": "vitest", - "test:it": "vitest integration", - "lint": "eslint . --ext .ts,.tsx", - "lint:autofix": "eslint . --ext .ts,.tsx --fix", - "format:check": "prettier --check src", - "format:write": "prettier --write src", - "start": "node --watch --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", - "build": "tsc" - }, - "keywords": [], - "author": "", - "license": "Apache-2.0", - "devDependencies": { - "@pagopa/eslint-config": "3.0.0", - "@types/dotenv-flow": "3.2.0", - "@types/node": "20.3.1", - "prettier": "2.8.8", - "ts-node": "10.9.2", - "typescript": "5.1.3", - "dotenv-flow": "3.2.0", - "kafkajs": "2.2.4", - "@types/uuid": "9.0.8", - "mkdirp": "3.0.1", - "tsc-esm-fix": "2.20.26", - "testcontainers": "10.2.2", - "vitest": "0.33.0" - }, - "dependencies": { - "@protobuf-ts/plugin": "2.9.4", - "@protobuf-ts/protoc": "2.9.4", - "@protobuf-ts/runtime": "2.9.4", - "@aws-sdk/client-sqs": "3.529.1", - "dotenv-flow": "3.2.0", - "kafka-iam-auth": "workspace:*", - "pagopa-interop-commons": "workspace:*", - "pagopa-interop-models": "workspace:^", - "ts-pattern": "5.0.6", - "uuid": "9.0.1", - "zod": "3.21.4" - } -} diff --git a/packages/notifier-seeder-purpose/src/config/notificationConfig.ts b/packages/notifier-seeder-purpose/src/config/notificationConfig.ts deleted file mode 100644 index c657cb2846..0000000000 --- a/packages/notifier-seeder-purpose/src/config/notificationConfig.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { z } from "zod"; - -export const NotificationConfig = z - .object({ - NOTIFICATION_QUEUE_URL: z.string(), - }) - .transform((c) => ({ - queueUrl: c.NOTIFICATION_QUEUE_URL, - })); -export type NotificationConfig = z.infer; - -export const notificationConfig = (): NotificationConfig => - NotificationConfig.parse(process.env); diff --git a/packages/notifier-seeder-purpose/src/index.ts b/packages/notifier-seeder-purpose/src/index.ts deleted file mode 100644 index 8743d65040..0000000000 --- a/packages/notifier-seeder-purpose/src/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* eslint-disable functional/immutable-data */ -import { runConsumer } from "kafka-iam-auth"; -import { EachMessagePayload } from "kafkajs"; -import { - PurposeTopicConfig, - kafkaConsumerConfig, - logger, - loggerConfig, - messageDecoderSupplier, - purposeTopicConfig, - runWithContext, -} from "pagopa-interop-commons"; -import { initQueueManager } from "./queue-manager/queueManager.js"; -import { notificationConfig } from "./config/notificationConfig.js"; -import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; -import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; - -const config = kafkaConsumerConfig(); -const topicsConfig = purposeTopicConfig(); -const logConfig = loggerConfig(); -const queueConfig = notificationConfig(); -const queueManager = initQueueManager({ - queueUrl: queueConfig.queueUrl, - messageGroupId: "message_group_all_notification", - logLevel: logConfig.logLevel, -}); - -export function processMessage(topicConfig: PurposeTopicConfig) { - return async (kafkaMessage: EachMessagePayload): Promise => { - const messageDecoder = messageDecoderSupplier( - topicConfig, - kafkaMessage.topic - ); - - const decodedMessage = messageDecoder(kafkaMessage.message); - - await runWithContext( - { - messageData: { - eventType: decodedMessage.type, - eventVersion: decodedMessage.event_version, - streamId: decodedMessage.stream_id, - }, - correlationId: decodedMessage.correlation_id, - }, - async () => { - if (decodedMessage.event_version !== 2) { - logger.info( - `Event with version ${decodedMessage.event_version} skipped` - ); - return; - } - - const purposeNotificationV1Event = - toPurposeEventNotification(decodedMessage); - const message = buildPurposeMessage( - decodedMessage, - purposeNotificationV1Event - ); - await queueManager.send(message); - - logger.info( - `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` - ); - } - ); - }; -} - -// to do: add purpose events - -await runConsumer( - config, - [topicsConfig.purposeTopic], - processMessage(topicsConfig) -); diff --git a/packages/notifier-seeder-purpose/src/notifierErrors.ts b/packages/notifier-seeder-purpose/src/notifierErrors.ts deleted file mode 100644 index 087f5e058f..0000000000 --- a/packages/notifier-seeder-purpose/src/notifierErrors.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { InternalError } from "pagopa-interop-models"; - -type NotifierErrorCode = "eventV1ConversionError"; - -export function eventV1ConversionError( - message: string -): InternalError { - return new InternalError({ - code: "eventV1ConversionError", - detail: `Error during event V1 conversion with message: ${message}`, - }); -} diff --git a/packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts b/packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts deleted file mode 100644 index 172fe174c8..0000000000 --- a/packages/notifier-seeder-purpose/src/queue-manager/queueManager.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { - ReceiveMessageCommand, - SQSClient, - SendMessageCommand, -} from "@aws-sdk/client-sqs"; -import { LoggerConfig, logger } from "pagopa-interop-commons"; -import { - queueManagerReceiveError, - queueManagerSendError, -} from "./queueManagerErrors.js"; -import { QueueMessage } from "./queueMessage.js"; - -export type QueueManager = { - send: (message: QueueMessage) => Promise; - receiveLast: (msgsToReceive?: number) => Promise; -}; - -export function initQueueManager( - config: { queueUrl: string; messageGroupId: string } & LoggerConfig -): QueueManager { - const client = new SQSClient({ - logger: config.logLevel === "debug" ? console : undefined, - }); - - return { - send: async (message: QueueMessage): Promise => { - logger.debug( - `Sending message ${message.messageUUID} to queue ${config.queueUrl}` - ); - try { - const response = await client.send( - new SendMessageCommand({ - QueueUrl: config.queueUrl, - MessageBody: JSON.stringify(message, (_, v) => - typeof v === "bigint" ? v.toString() : v - ), - MessageGroupId: config.messageGroupId, - MessageDeduplicationId: `${message.eventJournalPersistenceId}_${message.eventJournalSequenceNumber}`, - }) - ); - if (!response.MessageId) { - throw new Error("Unexpected empty response from SQS"); - } - return response.MessageId; - } catch (error) { - throw queueManagerSendError(config.queueUrl, error); - } - }, - receiveLast: async (msgsToReceive: number = 1): Promise => { - logger.debug( - `Receiving last ${msgsToReceive} messages from queue ${config.queueUrl}` - ); - try { - const response = await client.send( - new ReceiveMessageCommand({ - QueueUrl: config.queueUrl, - MaxNumberOfMessages: msgsToReceive, - }) - ); - - if (!response.Messages) { - throw new Error("Unexpected empty response from SQS"); - } - - return ( - response.Messages?.map((message) => message.Body) - .filter((body): body is string => body !== undefined) - .map((body) => QueueMessage.parse(JSON.parse(body))) ?? undefined - ); - } catch (error) { - throw queueManagerReceiveError(config.queueUrl, error); - } - }, - }; -} diff --git a/packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts b/packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts deleted file mode 100644 index 4c96d0ed9c..0000000000 --- a/packages/notifier-seeder-purpose/src/queue-manager/queueManagerErrors.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { InternalError, parseErrorMessage } from "pagopa-interop-models"; - -type QueueManagerErrorCode = - | "queueManagerSendError" - | "queueManagerReceiveError"; - -export class QueueManagerError extends InternalError { - constructor({ - code, - detail, - }: { - code: QueueManagerErrorCode; - detail: string; - }) { - super({ code, detail }); - } -} - -export function queueManagerSendError( - queueUrl: string, - error: unknown -): QueueManagerError { - return new QueueManagerError({ - code: "queueManagerSendError", - detail: `Error sending message to queue ${queueUrl}: ${parseErrorMessage( - error - )}`, - }); -} - -export function queueManagerReceiveError( - queueUrl: string, - error: unknown -): QueueManagerError { - return new QueueManagerError({ - code: "queueManagerReceiveError", - detail: `Error receiving message from queue ${queueUrl}: ${parseErrorMessage( - error - )}`, - }); -} diff --git a/packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts b/packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts deleted file mode 100644 index 95c3eb7eab..0000000000 --- a/packages/notifier-seeder-purpose/src/queue-manager/queueMessage.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from "zod"; - -export const QueueMessage = z.object({ - messageUUID: z.string().uuid(), - eventJournalPersistenceId: z.string(), - eventJournalSequenceNumber: z.number(), - eventTimestamp: z.number(), - kind: z.string(), - payload: z.unknown(), -}); - -export type QueueMessage = z.infer; diff --git a/packages/notifier-seeder-purpose/test/tsconfig.json b/packages/notifier-seeder-purpose/test/tsconfig.json deleted file mode 100644 index f85162abb0..0000000000 --- a/packages/notifier-seeder-purpose/test/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../tsconfig.json", - "include": ["."], -} diff --git a/packages/notifier-seeder-purpose/test/utils.ts b/packages/notifier-seeder-purpose/test/utils.ts deleted file mode 100644 index 75c4afc1e6..0000000000 --- a/packages/notifier-seeder-purpose/test/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { GenericContainer } from "testcontainers"; - -export const TEST_ELASTIC_MQ_IMAGE = "softwaremill/elasticmq-native:1.5.7"; -export const TEST_ELASTIC_MQ_PORT = 9324; - -export const elasticMQContainer = (): GenericContainer => - new GenericContainer(TEST_ELASTIC_MQ_IMAGE) - .withCopyFilesToContainer([ - { - source: "elasticmq.local.conf", - target: "/opt/elasticmq.conf", - }, - ]) - .withExposedPorts(TEST_ELASTIC_MQ_PORT); diff --git a/packages/notifier-seeder-purpose/tsconfig.json b/packages/notifier-seeder-purpose/tsconfig.json deleted file mode 100644 index 039e0b4d16..0000000000 --- a/packages/notifier-seeder-purpose/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "dist" - }, - "include": ["src"] -} diff --git a/packages/notifier-seeder-purpose/vitest.config.ts b/packages/notifier-seeder-purpose/vitest.config.ts deleted file mode 100644 index 498a780952..0000000000 --- a/packages/notifier-seeder-purpose/vitest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - setupFiles: ["dotenv/config"], - testTimeout: 60000, - hookTimeout: 60000, - }, -}); diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 4eaaf4f725..fe4f5215f9 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -3,20 +3,26 @@ import { runConsumer } from "kafka-iam-auth"; import { EachMessagePayload } from "kafkajs"; import { CatalogTopicConfig, + PurposeTopicConfig, catalogTopicConfig, kafkaConsumerConfig, logger, loggerConfig, messageDecoderSupplier, + purposeTopicConfig, runWithContext, } from "pagopa-interop-commons"; +import { match } from "ts-pattern"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; import { notificationConfig } from "./config/notificationConfig.js"; +import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; +import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; const config = kafkaConsumerConfig(); -const topicsConfig = catalogTopicConfig(); +const catalogTopicConf = catalogTopicConfig(); +const purposeTopicConf = purposeTopicConfig(); const logConfig = loggerConfig(); const queueConfig = notificationConfig(); const queueManager = initQueueManager({ @@ -25,12 +31,33 @@ const queueManager = initQueueManager({ logLevel: logConfig.logLevel, }); -export function processMessage(topicConfig: CatalogTopicConfig) { +export function processMessage( + catalogTopicConfig: CatalogTopicConfig, + purposeTopicConfig: PurposeTopicConfig +) { return async (kafkaMessage: EachMessagePayload): Promise => { - const messageDecoder = messageDecoderSupplier( - topicConfig, + const { messageDecoder, eventParser, messageBuilder } = match( kafkaMessage.topic - ); + ) + .with(catalogTopicConfig.catalogTopic, () => ({ + messageDecoder: messageDecoderSupplier( + catalogTopicConfig, + kafkaMessage.topic + ), + eventParser: toCatalogItemEventNotification, + messageBuilder: buildCatalogMessage, + })) + .with(purposeTopicConfig.purposeTopic, () => ({ + messageDecoder: messageDecoderSupplier( + purposeTopicConfig, + kafkaMessage.topic + ), + eventParser: toPurposeEventNotification, + messageBuilder: buildPurposeMessage, + })) + .otherwise(() => { + throw new Error(`Unknown topic: ${kafkaMessage.topic}`); + }); const decodedMessage = messageDecoder(kafkaMessage.message); @@ -51,8 +78,8 @@ export function processMessage(topicConfig: CatalogTopicConfig) { return; } - const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); - const message = buildCatalogMessage(decodedMessage, eserviceV1Event); + const event = eventParser(decodedMessage); + const message = messageBuilder(decodedMessage, event); await queueManager.send(message); logger.info( @@ -65,6 +92,6 @@ export function processMessage(topicConfig: CatalogTopicConfig) { await runConsumer( config, - [topicsConfig.catalogTopic], - processMessage(topicsConfig) + [catalogTopicConf.catalogTopic, purposeTopicConf.purposeTopic], + processMessage(catalogTopicConf, purposeTopicConf) ); diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotification.ts b/packages/notifier-seeder/src/models/purposeEventNotification.ts similarity index 100% rename from packages/notifier-seeder-purpose/src/models/purposeEventNotification.ts rename to packages/notifier-seeder/src/models/purposeEventNotification.ts diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts b/packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts similarity index 100% rename from packages/notifier-seeder-purpose/src/models/purposeEventNotificationConverter.ts rename to packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMappers.ts b/packages/notifier-seeder/src/models/purposeEventNotificationMappers.ts similarity index 100% rename from packages/notifier-seeder-purpose/src/models/purposeEventNotificationMappers.ts rename to packages/notifier-seeder/src/models/purposeEventNotificationMappers.ts diff --git a/packages/notifier-seeder-purpose/src/models/purposeEventNotificationMessage.ts b/packages/notifier-seeder/src/models/purposeEventNotificationMessage.ts similarity index 100% rename from packages/notifier-seeder-purpose/src/models/purposeEventNotificationMessage.ts rename to packages/notifier-seeder/src/models/purposeEventNotificationMessage.ts From 1a4e4d71d6c3a014de195dae10141935fde46347 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:25:46 +0200 Subject: [PATCH 412/537] Fix pnpm-lock file --- pnpm-lock.yaml | 73 -------------------------------------------------- 1 file changed, 73 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cc5c0b2ce1..a8f4ae21cf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -879,79 +879,6 @@ importers: specifier: 0.33.0 version: 0.33.0 - packages/notifier-seeder-purpose: - dependencies: - '@aws-sdk/client-sqs': - specifier: 3.529.1 - version: 3.529.1 - '@protobuf-ts/plugin': - specifier: 2.9.4 - version: 2.9.4 - '@protobuf-ts/protoc': - specifier: 2.9.4 - version: 2.9.4 - '@protobuf-ts/runtime': - specifier: 2.9.4 - version: 2.9.4 - dotenv-flow: - specifier: 3.2.0 - version: 3.2.0 - kafka-iam-auth: - specifier: workspace:* - version: link:../kafka-iam-auth - pagopa-interop-commons: - specifier: workspace:* - version: link:../commons - pagopa-interop-models: - specifier: workspace:^ - version: link:../models - ts-pattern: - specifier: 5.0.6 - version: 5.0.6 - uuid: - specifier: 9.0.1 - version: 9.0.1 - zod: - specifier: 3.21.4 - version: 3.21.4 - devDependencies: - '@pagopa/eslint-config': - specifier: 3.0.0 - version: 3.0.0(typescript@5.1.3) - '@types/dotenv-flow': - specifier: 3.2.0 - version: 3.2.0 - '@types/node': - specifier: 20.3.1 - version: 20.3.1 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 - kafkajs: - specifier: 2.2.4 - version: 2.2.4 - mkdirp: - specifier: 3.0.1 - version: 3.0.1 - prettier: - specifier: 2.8.8 - version: 2.8.8 - testcontainers: - specifier: 10.2.2 - version: 10.2.2 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.3.1)(typescript@5.1.3) - tsc-esm-fix: - specifier: 2.20.26 - version: 2.20.26 - typescript: - specifier: 5.1.3 - version: 5.1.3 - vitest: - specifier: 0.33.0 - version: 0.33.0 - packages/purpose-process: dependencies: '@zodios/core': From f445228773f991e2c9b3cc16a49882e1d5549b9a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:30:08 +0200 Subject: [PATCH 413/537] Revert changes --- packages/commons/src/kafka/kafka.ts | 4 ---- packages/notifier-seeder/src/index.ts | 29 +++++---------------------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index 2d51c8eca7..1b810bf793 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -47,10 +47,6 @@ export const messageDecoderSupplier = ( () => (message: KafkaMessage) => decodeKafkaMessage(message, EServiceEvent) ) - .with( - { purposeTopic: P.string }, - () => (message: KafkaMessage) => decodeKafkaMessage(message, PurposeEvent) - ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); }); diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index fe4f5215f9..37013b7c2e 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -36,28 +36,10 @@ export function processMessage( purposeTopicConfig: PurposeTopicConfig ) { return async (kafkaMessage: EachMessagePayload): Promise => { - const { messageDecoder, eventParser, messageBuilder } = match( + const messageDecoder = messageDecoderSupplier( + catalogTopicConf, kafkaMessage.topic - ) - .with(catalogTopicConfig.catalogTopic, () => ({ - messageDecoder: messageDecoderSupplier( - catalogTopicConfig, - kafkaMessage.topic - ), - eventParser: toCatalogItemEventNotification, - messageBuilder: buildCatalogMessage, - })) - .with(purposeTopicConfig.purposeTopic, () => ({ - messageDecoder: messageDecoderSupplier( - purposeTopicConfig, - kafkaMessage.topic - ), - eventParser: toPurposeEventNotification, - messageBuilder: buildPurposeMessage, - })) - .otherwise(() => { - throw new Error(`Unknown topic: ${kafkaMessage.topic}`); - }); + ); const decodedMessage = messageDecoder(kafkaMessage.message); @@ -78,9 +60,8 @@ export function processMessage( return; } - const event = eventParser(decodedMessage); - const message = messageBuilder(decodedMessage, event); - await queueManager.send(message); + const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); + const message = buildCatalogMessage(decodedMessage, eserviceV1Event); logger.info( `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` From 8b3930c5bbb2b8ba58c025c62e4c1ecfe26c8a78 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:32:57 +0200 Subject: [PATCH 414/537] Revert --- packages/notifier-seeder/src/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 37013b7c2e..7d2e8f5f27 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -12,13 +12,10 @@ import { purposeTopicConfig, runWithContext, } from "pagopa-interop-commons"; -import { match } from "ts-pattern"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; import { notificationConfig } from "./config/notificationConfig.js"; -import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; -import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; const config = kafkaConsumerConfig(); const catalogTopicConf = catalogTopicConfig(); @@ -33,11 +30,11 @@ const queueManager = initQueueManager({ export function processMessage( catalogTopicConfig: CatalogTopicConfig, - purposeTopicConfig: PurposeTopicConfig + _purposeTopicConfig: PurposeTopicConfig ) { return async (kafkaMessage: EachMessagePayload): Promise => { const messageDecoder = messageDecoderSupplier( - catalogTopicConf, + catalogTopicConfig, kafkaMessage.topic ); @@ -62,6 +59,7 @@ export function processMessage( const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); const message = buildCatalogMessage(decodedMessage, eserviceV1Event); + await queueManager.send(message); logger.info( `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` From 1c682c76afd517633f5b545bb459795b61903edf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:42:04 +0200 Subject: [PATCH 415/537] Draft --- packages/commons/src/kafka/kafka.ts | 4 +++ packages/notifier-seeder/src/index.ts | 19 ++++++++++-- .../src/models/purposeEventNotification.ts | 4 +-- .../purposeEventNotificationConverter.ts | 30 ++++++++++++------- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index 1b810bf793..2d51c8eca7 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -47,6 +47,10 @@ export const messageDecoderSupplier = ( () => (message: KafkaMessage) => decodeKafkaMessage(message, EServiceEvent) ) + .with( + { purposeTopic: P.string }, + () => (message: KafkaMessage) => decodeKafkaMessage(message, PurposeEvent) + ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); }); diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 7d2e8f5f27..aa266c021f 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -16,6 +16,8 @@ import { toCatalogItemEventNotification } from "./models/catalogItemEventNotific import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; import { notificationConfig } from "./config/notificationConfig.js"; +import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; +import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; const config = kafkaConsumerConfig(); const catalogTopicConf = catalogTopicConfig(); @@ -30,7 +32,7 @@ const queueManager = initQueueManager({ export function processMessage( catalogTopicConfig: CatalogTopicConfig, - _purposeTopicConfig: PurposeTopicConfig + purposeTopicConfig: PurposeTopicConfig ) { return async (kafkaMessage: EachMessagePayload): Promise => { const messageDecoder = messageDecoderSupplier( @@ -38,8 +40,13 @@ export function processMessage( kafkaMessage.topic ); - const decodedMessage = messageDecoder(kafkaMessage.message); + const messageDecoderPurpose = messageDecoderSupplier( + purposeTopicConfig, + kafkaMessage.topic + ); + const decodedMessage = messageDecoder(kafkaMessage.message); + const decodedMessagePurpose = messageDecoderPurpose(kafkaMessage.message); await runWithContext( { messageData: { @@ -59,7 +66,15 @@ export function processMessage( const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); const message = buildCatalogMessage(decodedMessage, eserviceV1Event); + const purposeV1Event = toPurposeEventNotification( + decodedMessagePurpose + ); + const messagePurpose = buildPurposeMessage( + decodedMessagePurpose, + purposeV1Event + ); await queueManager.send(message); + await queueManager.send(messagePurpose); logger.info( `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` diff --git a/packages/notifier-seeder/src/models/purposeEventNotification.ts b/packages/notifier-seeder/src/models/purposeEventNotification.ts index b43c3f1ba9..0acf2a9ee1 100644 --- a/packages/notifier-seeder/src/models/purposeEventNotification.ts +++ b/packages/notifier-seeder/src/models/purposeEventNotification.ts @@ -45,7 +45,7 @@ export type PurposeV1Notification = Omit< // PurposeVersionSuspendedV1 // PurposeVersionArchivedV1 // PurposeVersionWaitedForApprovalV1 -export type PurposeCreatedNotification = { +export type PurposeNotification = { purpose: PurposeV1Notification; }; @@ -77,7 +77,7 @@ export type PurposeIdAndVersionIdNotification = { }; export type PurposeEventNotification = - | PurposeCreatedNotification + | PurposeNotification | PurposeAndVersionIdNotification | PurposeIdNotification | PurposeIdAndVersionIdNotification; diff --git a/packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts b/packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts index a382e75156..6e10b0a1e3 100644 --- a/packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts +++ b/packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts @@ -5,7 +5,11 @@ import { } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { + PurposeAndVersionIdNotification, PurposeEventNotification, + PurposeIdAndVersionIdNotification, + PurposeIdNotification, + PurposeNotification, PurposeV1Notification, } from "./purposeEventNotification.js"; import { toPurposeV1Notification } from "./purposeEventNotificationMappers.js"; @@ -37,23 +41,29 @@ export const toPurposeEventNotification = ( { type: "PurposeCloned" }, { type: "NewPurposeVersionWaitingForApproval" }, { type: "PurposeVersionOverQuotaUnsuspended" }, - (event) => ({ + (event): PurposeNotification => ({ purpose: getPurpose(event), }) ) - .with({ type: "PurposeVersionRejected" }, (event) => ({ - purpose: getPurpose(event), - versionId: event.data.versionId, - })) + .with( + { type: "PurposeVersionRejected" }, + (event): PurposeAndVersionIdNotification => ({ + purpose: getPurpose(event), + versionId: event.data.versionId, + }) + ) .with( { type: "DraftPurposeDeleted" }, { type: "WaitingForApprovalPurposeDeleted" }, - (event) => ({ + (event): PurposeIdNotification => ({ + purposeId: getPurpose(event).id, + }) + ) + .with( + { type: "WaitingForApprovalPurposeVersionDeleted" }, + (event): PurposeIdAndVersionIdNotification => ({ purposeId: getPurpose(event).id, + versionId: event.data.versionId, }) ) - .with({ type: "WaitingForApprovalPurposeVersionDeleted" }, (event) => ({ - purposeId: getPurpose(event).id, - versionId: event.data.versionId, - })) .exhaustive(); From ac3fc02cfbdbbc16e56807b0ee17f638e42cc2de Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:53:45 +0200 Subject: [PATCH 416/537] Draft --- packages/commons/src/kafka/kafka.ts | 11 +++++-- packages/notifier-seeder/src/index.ts | 44 +++++++++++++++------------ 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index 2d51c8eca7..28577a91b6 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -1,7 +1,13 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { z } from "zod"; import { EachMessagePayload, KafkaMessage } from "kafkajs"; -import { EServiceEvent, Message, PurposeEvent } from "pagopa-interop-models"; +import { + EServiceEvent, + EServiceEventV2, + Message, + PurposeEvent, + PurposeEventV2, +} from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { KafkaTopicConfig } from "../config/kafkaTopicConfig.js"; @@ -49,7 +55,8 @@ export const messageDecoderSupplier = ( ) .with( { purposeTopic: P.string }, - () => (message: KafkaMessage) => decodeKafkaMessage(message, PurposeEvent) + () => (message: KafkaMessage) => + decodeKafkaMessage(message, PurposeEventV2) ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index aa266c021f..fe4f5215f9 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -12,6 +12,7 @@ import { purposeTopicConfig, runWithContext, } from "pagopa-interop-commons"; +import { match } from "ts-pattern"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; @@ -35,18 +36,31 @@ export function processMessage( purposeTopicConfig: PurposeTopicConfig ) { return async (kafkaMessage: EachMessagePayload): Promise => { - const messageDecoder = messageDecoderSupplier( - catalogTopicConfig, + const { messageDecoder, eventParser, messageBuilder } = match( kafkaMessage.topic - ); - - const messageDecoderPurpose = messageDecoderSupplier( - purposeTopicConfig, - kafkaMessage.topic - ); + ) + .with(catalogTopicConfig.catalogTopic, () => ({ + messageDecoder: messageDecoderSupplier( + catalogTopicConfig, + kafkaMessage.topic + ), + eventParser: toCatalogItemEventNotification, + messageBuilder: buildCatalogMessage, + })) + .with(purposeTopicConfig.purposeTopic, () => ({ + messageDecoder: messageDecoderSupplier( + purposeTopicConfig, + kafkaMessage.topic + ), + eventParser: toPurposeEventNotification, + messageBuilder: buildPurposeMessage, + })) + .otherwise(() => { + throw new Error(`Unknown topic: ${kafkaMessage.topic}`); + }); const decodedMessage = messageDecoder(kafkaMessage.message); - const decodedMessagePurpose = messageDecoderPurpose(kafkaMessage.message); + await runWithContext( { messageData: { @@ -64,17 +78,9 @@ export function processMessage( return; } - const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); - const message = buildCatalogMessage(decodedMessage, eserviceV1Event); - const purposeV1Event = toPurposeEventNotification( - decodedMessagePurpose - ); - const messagePurpose = buildPurposeMessage( - decodedMessagePurpose, - purposeV1Event - ); + const event = eventParser(decodedMessage); + const message = messageBuilder(decodedMessage, event); await queueManager.send(message); - await queueManager.send(messagePurpose); logger.info( `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` From d89452724479e80ea3096dbe3f6aeaa33f0feb75 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 16:55:02 +0200 Subject: [PATCH 417/537] Revert --- packages/notifier-seeder/src/index.ts | 50 +++++++-------------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index fe4f5215f9..d74f795379 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -3,26 +3,20 @@ import { runConsumer } from "kafka-iam-auth"; import { EachMessagePayload } from "kafkajs"; import { CatalogTopicConfig, - PurposeTopicConfig, catalogTopicConfig, kafkaConsumerConfig, logger, loggerConfig, messageDecoderSupplier, - purposeTopicConfig, - runWithContext, + runWithLoggerContext, } from "pagopa-interop-commons"; -import { match } from "ts-pattern"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; import { notificationConfig } from "./config/notificationConfig.js"; -import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; -import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; const config = kafkaConsumerConfig(); -const catalogTopicConf = catalogTopicConfig(); -const purposeTopicConf = purposeTopicConfig(); +const topicsConfig = catalogTopicConfig(); const logConfig = loggerConfig(); const queueConfig = notificationConfig(); const queueManager = initQueueManager({ @@ -31,38 +25,18 @@ const queueManager = initQueueManager({ logLevel: logConfig.logLevel, }); -export function processMessage( - catalogTopicConfig: CatalogTopicConfig, - purposeTopicConfig: PurposeTopicConfig -) { +export function processMessage(topicConfig: CatalogTopicConfig) { return async (kafkaMessage: EachMessagePayload): Promise => { - const { messageDecoder, eventParser, messageBuilder } = match( + const messageDecoder = messageDecoderSupplier( + topicConfig, kafkaMessage.topic - ) - .with(catalogTopicConfig.catalogTopic, () => ({ - messageDecoder: messageDecoderSupplier( - catalogTopicConfig, - kafkaMessage.topic - ), - eventParser: toCatalogItemEventNotification, - messageBuilder: buildCatalogMessage, - })) - .with(purposeTopicConfig.purposeTopic, () => ({ - messageDecoder: messageDecoderSupplier( - purposeTopicConfig, - kafkaMessage.topic - ), - eventParser: toPurposeEventNotification, - messageBuilder: buildPurposeMessage, - })) - .otherwise(() => { - throw new Error(`Unknown topic: ${kafkaMessage.topic}`); - }); + ); const decodedMessage = messageDecoder(kafkaMessage.message); - await runWithContext( + await runWithLoggerContext( { + serviceName: "notifier-seeder", messageData: { eventType: decodedMessage.type, eventVersion: decodedMessage.event_version, @@ -78,8 +52,8 @@ export function processMessage( return; } - const event = eventParser(decodedMessage); - const message = messageBuilder(decodedMessage, event); + const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); + const message = buildCatalogMessage(decodedMessage, eserviceV1Event); await queueManager.send(message); logger.info( @@ -92,6 +66,6 @@ export function processMessage( await runConsumer( config, - [catalogTopicConf.catalogTopic, purposeTopicConf.purposeTopic], - processMessage(catalogTopicConf, purposeTopicConf) + [topicsConfig.catalogTopic], + processMessage(topicsConfig) ); From ba7d89dfb875f4ab8c75e535205cc1559a4cde32 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 17:06:12 +0200 Subject: [PATCH 418/537] Revert --- packages/commons/src/kafka/kafka.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index 28577a91b6..a3c387f8b9 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -1,13 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { z } from "zod"; import { EachMessagePayload, KafkaMessage } from "kafkajs"; -import { - EServiceEvent, - EServiceEventV2, - Message, - PurposeEvent, - PurposeEventV2, -} from "pagopa-interop-models"; +import { EServiceEvent, Message, PurposeEventV2 } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { KafkaTopicConfig } from "../config/kafkaTopicConfig.js"; @@ -53,11 +47,6 @@ export const messageDecoderSupplier = ( () => (message: KafkaMessage) => decodeKafkaMessage(message, EServiceEvent) ) - .with( - { purposeTopic: P.string }, - () => (message: KafkaMessage) => - decodeKafkaMessage(message, PurposeEventV2) - ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); }); From 783560ee5507b36a3890c6400341217d96429832 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 17:08:09 +0200 Subject: [PATCH 419/537] Revert --- packages/commons/src/kafka/kafka.ts | 2 +- packages/notifier-seeder/src/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index a3c387f8b9..94f195c0f6 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { z } from "zod"; import { EachMessagePayload, KafkaMessage } from "kafkajs"; -import { EServiceEvent, Message, PurposeEventV2 } from "pagopa-interop-models"; +import { EServiceEvent, Message } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { KafkaTopicConfig } from "../config/kafkaTopicConfig.js"; diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index d74f795379..be5498299d 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -8,6 +8,7 @@ import { logger, loggerConfig, messageDecoderSupplier, + runWithContext, runWithLoggerContext, } from "pagopa-interop-commons"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; @@ -34,9 +35,8 @@ export function processMessage(topicConfig: CatalogTopicConfig) { const decodedMessage = messageDecoder(kafkaMessage.message); - await runWithLoggerContext( + await runWithContext( { - serviceName: "notifier-seeder", messageData: { eventType: decodedMessage.type, eventVersion: decodedMessage.event_version, From 6d5f0e15e1b958833aab83be6ddadd457ce50494 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 17:08:31 +0200 Subject: [PATCH 420/537] Fix import --- packages/notifier-seeder/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index be5498299d..4eaaf4f725 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -9,7 +9,6 @@ import { loggerConfig, messageDecoderSupplier, runWithContext, - runWithLoggerContext, } from "pagopa-interop-commons"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; From 839e56c62b83cd252b60745e6c4b9f9e445b65e2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 17:09:39 +0200 Subject: [PATCH 421/537] Draft --- packages/commons/src/kafka/kafka.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index 94f195c0f6..afc91d6da0 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { z } from "zod"; import { EachMessagePayload, KafkaMessage } from "kafkajs"; -import { EServiceEvent, Message } from "pagopa-interop-models"; +import { EServiceEvent, Message, PurposeEventV2 } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { KafkaTopicConfig } from "../config/kafkaTopicConfig.js"; @@ -47,6 +47,11 @@ export const messageDecoderSupplier = ( () => (message: KafkaMessage) => decodeKafkaMessage(message, EServiceEvent) ) + .with( + { purposeTopic: P.string }, + () => (message: KafkaMessage) => + decodeKafkaMessage(message, PurposeEventV2) + ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); }); From f6f56aacf50cd1ac317ce1e28f8ce15e26b91e65 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 7 May 2024 17:27:27 +0200 Subject: [PATCH 422/537] Refactor --- packages/commons/src/kafka/kafka.ts | 5 +- packages/notifier-seeder/src/index.ts | 114 ++++++++++++++++++-------- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/packages/commons/src/kafka/kafka.ts b/packages/commons/src/kafka/kafka.ts index afc91d6da0..2d51c8eca7 100644 --- a/packages/commons/src/kafka/kafka.ts +++ b/packages/commons/src/kafka/kafka.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { z } from "zod"; import { EachMessagePayload, KafkaMessage } from "kafkajs"; -import { EServiceEvent, Message, PurposeEventV2 } from "pagopa-interop-models"; +import { EServiceEvent, Message, PurposeEvent } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { KafkaTopicConfig } from "../config/kafkaTopicConfig.js"; @@ -49,8 +49,7 @@ export const messageDecoderSupplier = ( ) .with( { purposeTopic: P.string }, - () => (message: KafkaMessage) => - decodeKafkaMessage(message, PurposeEventV2) + () => (message: KafkaMessage) => decodeKafkaMessage(message, PurposeEvent) ) .otherwise(() => { throw new Error(`Topic decoder not found for provided topic : ${topic}`); diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 4eaaf4f725..a721f1196d 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -3,20 +3,27 @@ import { runConsumer } from "kafka-iam-auth"; import { EachMessagePayload } from "kafkajs"; import { CatalogTopicConfig, + PurposeTopicConfig, catalogTopicConfig, + decodeKafkaMessage, kafkaConsumerConfig, logger, loggerConfig, - messageDecoderSupplier, + purposeTopicConfig, runWithContext, } from "pagopa-interop-commons"; +import { match } from "ts-pattern"; +import { EServiceEvent, PurposeEventV2 } from "pagopa-interop-models"; import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; import { notificationConfig } from "./config/notificationConfig.js"; +import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; +import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; const config = kafkaConsumerConfig(); -const topicsConfig = catalogTopicConfig(); +const catalogTopicConf = catalogTopicConfig(); +const purposeTopicConf = purposeTopicConfig(); const logConfig = loggerConfig(); const queueConfig = notificationConfig(); const queueManager = initQueueManager({ @@ -25,46 +32,87 @@ const queueManager = initQueueManager({ logLevel: logConfig.logLevel, }); -export function processMessage(topicConfig: CatalogTopicConfig) { +export function processMessage( + _catalogTopic: CatalogTopicConfig, + _purposeTopic: PurposeTopicConfig +) { return async (kafkaMessage: EachMessagePayload): Promise => { - const messageDecoder = messageDecoderSupplier( - topicConfig, - kafkaMessage.topic - ); + match(kafkaMessage.topic) + .with("catalog", async () => { + const decodedMessage = decodeKafkaMessage( + kafkaMessage.message, + EServiceEvent + ); + + await runWithContext( + { + messageData: { + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + }, + correlationId: decodedMessage.correlation_id, + }, + async () => { + if (decodedMessage.event_version !== 2) { + logger.info( + `Event with version ${decodedMessage.event_version} skipped` + ); + return; + } + + const eserviceV1Event = + toCatalogItemEventNotification(decodedMessage); + const message = buildCatalogMessage( + decodedMessage, + eserviceV1Event + ); + await queueManager.send(message); - const decodedMessage = messageDecoder(kafkaMessage.message); + logger.info( + `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` + ); + } + ); + }) + .with("purpose", async () => { + const decodedMessage = decodeKafkaMessage( + kafkaMessage.message, + PurposeEventV2 + ); - await runWithContext( - { - messageData: { - eventType: decodedMessage.type, - eventVersion: decodedMessage.event_version, - streamId: decodedMessage.stream_id, - }, - correlationId: decodedMessage.correlation_id, - }, - async () => { - if (decodedMessage.event_version !== 2) { - logger.info( - `Event with version ${decodedMessage.event_version} skipped` - ); - return; - } + await runWithContext( + { + messageData: { + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + }, + correlationId: decodedMessage.correlation_id, + }, + async () => { + if (decodedMessage.event_version !== 2) { + logger.info( + `Event with version ${decodedMessage.event_version} skipped` + ); + return; + } - const eserviceV1Event = toCatalogItemEventNotification(decodedMessage); - const message = buildCatalogMessage(decodedMessage, eserviceV1Event); - await queueManager.send(message); + const purposeV1Event = toPurposeEventNotification(decodedMessage); + const message = buildPurposeMessage(decodedMessage, purposeV1Event); + await queueManager.send(message); - logger.info( - `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` + logger.info( + `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` + ); + } ); - } - ); + }); }; } await runConsumer( config, - [topicsConfig.catalogTopic], - processMessage(topicsConfig) + [catalogTopicConf.catalogTopic, purposeTopicConf.purposeTopic], + processMessage(catalogTopicConf, purposeTopicConf) ); From d3c5e70a73685b565654233dd35c0859efb75efb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 08:54:33 +0200 Subject: [PATCH 423/537] Refactor --- packages/notifier-seeder/src/index.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index a721f1196d..43faed15f4 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -33,12 +33,12 @@ const queueManager = initQueueManager({ }); export function processMessage( - _catalogTopic: CatalogTopicConfig, - _purposeTopic: PurposeTopicConfig + catalogTopic: CatalogTopicConfig, + purposeTopic: PurposeTopicConfig ) { return async (kafkaMessage: EachMessagePayload): Promise => { - match(kafkaMessage.topic) - .with("catalog", async () => { + await match(kafkaMessage.topic) + .with(catalogTopic.catalogTopic, async () => { const decodedMessage = decodeKafkaMessage( kafkaMessage.message, EServiceEvent @@ -75,7 +75,7 @@ export function processMessage( } ); }) - .with("purpose", async () => { + .with(purposeTopic.purposeTopic, async () => { const decodedMessage = decodeKafkaMessage( kafkaMessage.message, PurposeEventV2 @@ -107,6 +107,9 @@ export function processMessage( ); } ); + }) + .otherwise(() => { + throw new Error(`Unknown topic: ${kafkaMessage.topic}`); }); }; } From 0e5cbfada2f053bcbe41ae1be3390ed449a9a5d7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 08:59:37 +0200 Subject: [PATCH 424/537] Refactor --- packages/notifier-seeder/src/index.ts | 8 ++++---- .../models/{ => catalog}/catalogItemEventNotification.ts | 2 +- .../catalogItemEventNotificationConverter.ts | 2 +- .../{ => catalog}/catalogItemEventNotificationMappers.ts | 2 +- .../{ => catalog}/catalogItemEventNotificationMessage.ts | 2 +- .../src/models/{ => purpose}/purposeEventNotification.ts | 0 .../{ => purpose}/purposeEventNotificationConverter.ts | 0 .../{ => purpose}/purposeEventNotificationMappers.ts | 0 .../{ => purpose}/purposeEventNotificationMessage.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename packages/notifier-seeder/src/models/{ => catalog}/catalogItemEventNotification.ts (99%) rename packages/notifier-seeder/src/models/{ => catalog}/catalogItemEventNotificationConverter.ts (99%) rename packages/notifier-seeder/src/models/{ => catalog}/catalogItemEventNotificationMappers.ts (98%) rename packages/notifier-seeder/src/models/{ => catalog}/catalogItemEventNotificationMessage.ts (97%) rename packages/notifier-seeder/src/models/{ => purpose}/purposeEventNotification.ts (100%) rename packages/notifier-seeder/src/models/{ => purpose}/purposeEventNotificationConverter.ts (100%) rename packages/notifier-seeder/src/models/{ => purpose}/purposeEventNotificationMappers.ts (100%) rename packages/notifier-seeder/src/models/{ => purpose}/purposeEventNotificationMessage.ts (96%) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 43faed15f4..d9cf590ee5 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -14,12 +14,12 @@ import { } from "pagopa-interop-commons"; import { match } from "ts-pattern"; import { EServiceEvent, PurposeEventV2 } from "pagopa-interop-models"; -import { toCatalogItemEventNotification } from "./models/catalogItemEventNotificationConverter.js"; -import { buildCatalogMessage } from "./models/catalogItemEventNotificationMessage.js"; +import { toCatalogItemEventNotification } from "./models/catalog/catalogItemEventNotificationConverter.js"; +import { buildCatalogMessage } from "./models/catalog/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; import { notificationConfig } from "./config/notificationConfig.js"; -import { toPurposeEventNotification } from "./models/purposeEventNotificationConverter.js"; -import { buildPurposeMessage } from "./models/purposeEventNotificationMessage.js"; +import { toPurposeEventNotification } from "./models/purpose/purposeEventNotificationConverter.js"; +import { buildPurposeMessage } from "./models/purpose/purposeEventNotificationMessage.js"; const config = kafkaConsumerConfig(); const catalogTopicConf = catalogTopicConfig(); diff --git a/packages/notifier-seeder/src/models/catalogItemEventNotification.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts similarity index 99% rename from packages/notifier-seeder/src/models/catalogItemEventNotification.ts rename to packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts index 240b9e10fe..b5aaf4717f 100644 --- a/packages/notifier-seeder/src/models/catalogItemEventNotification.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts @@ -4,7 +4,7 @@ import { CatalogDocumentV1, CatalogItemRiskAnalysisV1, CatalogItemV1, -} from "../gen/v1/events.js"; +} from "../../gen/v1/events.js"; /* The notification catalog event types was implemented to allow compatibility with old notifier consumers, diff --git a/packages/notifier-seeder/src/models/catalogItemEventNotificationConverter.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts similarity index 99% rename from packages/notifier-seeder/src/models/catalogItemEventNotificationConverter.ts rename to packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts index b1e2362297..ddff11f8a3 100644 --- a/packages/notifier-seeder/src/models/catalogItemEventNotificationConverter.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts @@ -4,7 +4,7 @@ import { missingKafkaMessageDataError, } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { eventV1ConversionError } from "../notifierErrors.js"; +import { eventV1ConversionError } from "../../notifierErrors.js"; import { CatalogDescriptorNotification, CatalogDescriptorV1Notification, diff --git a/packages/notifier-seeder/src/models/catalogItemEventNotificationMappers.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts similarity index 98% rename from packages/notifier-seeder/src/models/catalogItemEventNotificationMappers.ts rename to packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts index d731cf2ee9..606bbab076 100644 --- a/packages/notifier-seeder/src/models/catalogItemEventNotificationMappers.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts @@ -13,7 +13,7 @@ import { eserviceMode, technology, } from "pagopa-interop-models"; -import { CatalogAttributeValueV1 } from "../gen/v1/events.js"; +import { CatalogAttributeValueV1 } from "../../gen/v1/events.js"; import { CatalogDescriptorV1Notification, CatalogDocumentV1Notification, diff --git a/packages/notifier-seeder/src/models/catalogItemEventNotificationMessage.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts similarity index 97% rename from packages/notifier-seeder/src/models/catalogItemEventNotificationMessage.ts rename to packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts index 2207744e01..cc665469b2 100644 --- a/packages/notifier-seeder/src/models/catalogItemEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts @@ -1,7 +1,7 @@ import { EServiceEventEnvelopeV2 } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { v4 as uuidv4 } from "uuid"; -import { QueueMessage } from "../queue-manager/queueMessage.js"; +import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { CatalogItemEventNotification } from "./catalogItemEventNotification.js"; export const eventV2TypeMapper = ( diff --git a/packages/notifier-seeder/src/models/purposeEventNotification.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotification.ts similarity index 100% rename from packages/notifier-seeder/src/models/purposeEventNotification.ts rename to packages/notifier-seeder/src/models/purpose/purposeEventNotification.ts diff --git a/packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationConverter.ts similarity index 100% rename from packages/notifier-seeder/src/models/purposeEventNotificationConverter.ts rename to packages/notifier-seeder/src/models/purpose/purposeEventNotificationConverter.ts diff --git a/packages/notifier-seeder/src/models/purposeEventNotificationMappers.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts similarity index 100% rename from packages/notifier-seeder/src/models/purposeEventNotificationMappers.ts rename to packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts diff --git a/packages/notifier-seeder/src/models/purposeEventNotificationMessage.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts similarity index 96% rename from packages/notifier-seeder/src/models/purposeEventNotificationMessage.ts rename to packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts index e40685bd91..a728240774 100644 --- a/packages/notifier-seeder/src/models/purposeEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts @@ -1,7 +1,7 @@ import { PurposeEventEnvelopeV2 } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { v4 as uuidv4 } from "uuid"; -import { QueueMessage } from "../queue-manager/queueMessage.js"; +import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { PurposeEventNotification } from "./purposeEventNotification.js"; export const eventV2TypeMapper = ( From 371f17c8e5157bebbfb2c7925367424a3eebe6ce Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 09:17:15 +0200 Subject: [PATCH 425/537] minor fix --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 2ace95494d..5ac155a941 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -320,7 +320,7 @@ export function purposeServiceBuilder( purposeId, reversePurposeUpdateContent, organizationId, - "Receive", + eserviceMode.receive, { readModelService, correlationId, repository } ); }, From acd0fa82df97f84e0497d407ed85ee1743fe9fad Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 09:41:52 +0200 Subject: [PATCH 426/537] Add test --- packages/notifier-seeder/package.json | 1 + .../notificationMessage.integration.test.ts | 54 +++++++++++++++++-- pnpm-lock.yaml | 3 ++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/notifier-seeder/package.json b/packages/notifier-seeder/package.json index 48b5bf7931..73bdaecdb9 100644 --- a/packages/notifier-seeder/package.json +++ b/packages/notifier-seeder/package.json @@ -32,6 +32,7 @@ "kafkajs": "2.2.4", "@types/uuid": "9.0.8", "mkdirp": "3.0.1", + "pagopa-interop-commons-test": "workspace:*", "tsc-esm-fix": "2.20.26", "testcontainers": "10.2.2", "vitest": "0.33.0" diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 32e534f91c..269711368e 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -1,28 +1,34 @@ /* eslint-disable functional/immutable-data */ /* eslint-disable functional/no-let */ import { StartedTestContainer } from "testcontainers"; -import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { EServiceDescriptorSuspendedV2, EServiceDescriptorV2, EServiceEventEnvelopeV2, EServiceV2, + PurposeAddedV2, + PurposeEventEnvelopeV2, descriptorState, eserviceMode, generateId, technology, toDescriptorV2, toEServiceV2, + toPurposeV2, unsafeBrandId, } from "pagopa-interop-models"; import { v4 } from "uuid"; +import { getMockPurpose } from "pagopa-interop-commons-test"; import { QueueManager, initQueueManager, } from "../src/queue-manager/queueManager.js"; -import { toCatalogItemEventNotification } from "../src/models/catalogItemEventNotificationConverter.js"; -import { buildCatalogMessage } from "../src/models/catalogItemEventNotificationMessage.js"; +import { toCatalogItemEventNotification } from "../src/models/catalog/catalogItemEventNotificationConverter.js"; +import { buildCatalogMessage } from "../src/models/catalog/catalogItemEventNotificationMessage.js"; +import { buildPurposeMessage } from "../src/models/purpose/purposeEventNotificationMessage.js"; +import { toPurposeEventNotification } from "../src/models/purpose/purposeEventNotificationConverter.js"; import { catalogItemDescriptorUpdatedNotification } from "./resources/catalogItemDescriptorUpdate.js"; import { TEST_ELASTIC_MQ_PORT, elasticMQContainer } from "./utils.js"; @@ -162,4 +168,46 @@ describe("Notification tests", async () => { ); }); }); + describe("Purpose Event Message", async () => { + it("should send a message to the queue", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockPurpose = getMockPurpose(); + + const eventV2: PurposeAddedV2 = { + purpose: toPurposeV2(mockPurpose), + }; + + const eventEnvelope: PurposeEventEnvelopeV2 = { + sequence_num: 1, + stream_id: mockPurpose.id, + version: 1, + correlation_id: v4(), + log_date: new Date(), + event_version: 2, + type: "PurposeAdded", + data: eventV2, + }; + const purposeEventNotification = + toPurposeEventNotification(eventEnvelope); + + const message = buildPurposeMessage( + eventEnvelope, + purposeEventNotification + ); + await queueWriter.send(message); + + const receivedMessage = (await queueWriter.receiveLast())[0]; + + expect(receivedMessage.payload).toEqual({ + purpose: { + ...mockPurpose, + createdAt: new Date().toISOString(), + }, + }); + + vi.useRealTimers(); + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a8f4ae21cf..512e6fec36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -860,6 +860,9 @@ importers: mkdirp: specifier: 3.0.1 version: 3.0.1 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test prettier: specifier: 2.8.8 version: 2.8.8 From 56070573cfbda2d6aba77a21e8abf6f65bdfe696 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 09:59:08 +0200 Subject: [PATCH 427/537] Update env file --- packages/notifier-seeder/.env | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/notifier-seeder/.env b/packages/notifier-seeder/.env index 13754ddc66..3654fe0c64 100644 --- a/packages/notifier-seeder/.env +++ b/packages/notifier-seeder/.env @@ -5,5 +5,6 @@ KAFKA_GROUP_ID="notifier-seeder--group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH=true CATALOG_TOPIC="event-store.catalog.events" +PURPOSE_TOPIC="event-store.purpose.events" AWS_REGION="eu-central-1" NOTIFICATION_QUEUE_URL="http://localhost:9324/000000000000/sqsLocalQueue.fifo" \ No newline at end of file From 176887194cb3aa76f683a661345df9e40bbad7e9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 10:25:12 +0200 Subject: [PATCH 428/537] Test --- .../test/notificationMessage.integration.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 269711368e..96d5e19da3 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -128,7 +128,7 @@ describe("Notification tests", async () => { }); describe("Update Descriptor Event Message", async () => { - it("should send a message to the queue", async () => { + it.only("should send a message to the queue", async () => { const descriptor = getDescriptorMock( "6b48e234-aac6-4d33-aef4-93816588ff41" ); @@ -163,9 +163,17 @@ describe("Notification tests", async () => { const receivedMessage = (await queueWriter.receiveLast())[0]; + await queueWriter.send(message); + + const receivedMessage2 = (await queueWriter.receiveLast())[0]; + expect(receivedMessage.payload).toEqual( catalogItemDescriptorUpdatedNotification.payload ); + + expect(receivedMessage2.payload).toEqual( + catalogItemDescriptorUpdatedNotification.payload + ); }); }); describe("Purpose Event Message", async () => { From cd7b8a130ba15d3ad4b6a6bfa943478a2262fd02 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 10:44:48 +0200 Subject: [PATCH 429/537] Test --- .../test/notificationMessage.integration.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 96d5e19da3..74cc296e17 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -126,9 +126,9 @@ describe("Notification tests", async () => { afterAll(async () => { await startedElasticMQContainer.stop(); }); - + /* describe("Update Descriptor Event Message", async () => { - it.only("should send a message to the queue", async () => { + it("should send a message to the queue", async () => { const descriptor = getDescriptorMock( "6b48e234-aac6-4d33-aef4-93816588ff41" ); @@ -176,8 +176,9 @@ describe("Notification tests", async () => { ); }); }); + */ describe("Purpose Event Message", async () => { - it("should send a message to the queue", async () => { + it.only("should send a message to the queue", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From 75d69cb7dc85cf0a0550ba3c888eef772061ca52 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 10:47:20 +0200 Subject: [PATCH 430/537] Test --- .../notificationMessage.integration.test.ts | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 74cc296e17..351ed8ca3a 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -4,20 +4,9 @@ import { StartedTestContainer } from "testcontainers"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { - EServiceDescriptorSuspendedV2, - EServiceDescriptorV2, - EServiceEventEnvelopeV2, - EServiceV2, PurposeAddedV2, PurposeEventEnvelopeV2, - descriptorState, - eserviceMode, - generateId, - technology, - toDescriptorV2, - toEServiceV2, toPurposeV2, - unsafeBrandId, } from "pagopa-interop-models"; import { v4 } from "uuid"; import { getMockPurpose } from "pagopa-interop-commons-test"; @@ -25,13 +14,11 @@ import { QueueManager, initQueueManager, } from "../src/queue-manager/queueManager.js"; -import { toCatalogItemEventNotification } from "../src/models/catalog/catalogItemEventNotificationConverter.js"; -import { buildCatalogMessage } from "../src/models/catalog/catalogItemEventNotificationMessage.js"; import { buildPurposeMessage } from "../src/models/purpose/purposeEventNotificationMessage.js"; import { toPurposeEventNotification } from "../src/models/purpose/purposeEventNotificationConverter.js"; -import { catalogItemDescriptorUpdatedNotification } from "./resources/catalogItemDescriptorUpdate.js"; import { TEST_ELASTIC_MQ_PORT, elasticMQContainer } from "./utils.js"; +/* const getDescriptorMock = (descriptorId: string): EServiceDescriptorV2 => toDescriptorV2({ id: unsafeBrandId(descriptorId), @@ -101,6 +88,7 @@ const getMockEService = (id: string): EServiceV2 => mode: eserviceMode.deliver, riskAnalysis: [], }); +*/ describe("Notification tests", async () => { process.env.AWS_CONFIG_FILE = "aws.config.local"; @@ -178,7 +166,7 @@ describe("Notification tests", async () => { }); */ describe("Purpose Event Message", async () => { - it.only("should send a message to the queue", async () => { + it("should send a message to the queue", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From 969428968ab3fe68dbe09b7953a84ecdf313d2bd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 10:54:02 +0200 Subject: [PATCH 431/537] Revert "Test" This reverts commit 75d69cb7dc85cf0a0550ba3c888eef772061ca52. --- .../notificationMessage.integration.test.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 351ed8ca3a..74cc296e17 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -4,9 +4,20 @@ import { StartedTestContainer } from "testcontainers"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { + EServiceDescriptorSuspendedV2, + EServiceDescriptorV2, + EServiceEventEnvelopeV2, + EServiceV2, PurposeAddedV2, PurposeEventEnvelopeV2, + descriptorState, + eserviceMode, + generateId, + technology, + toDescriptorV2, + toEServiceV2, toPurposeV2, + unsafeBrandId, } from "pagopa-interop-models"; import { v4 } from "uuid"; import { getMockPurpose } from "pagopa-interop-commons-test"; @@ -14,11 +25,13 @@ import { QueueManager, initQueueManager, } from "../src/queue-manager/queueManager.js"; +import { toCatalogItemEventNotification } from "../src/models/catalog/catalogItemEventNotificationConverter.js"; +import { buildCatalogMessage } from "../src/models/catalog/catalogItemEventNotificationMessage.js"; import { buildPurposeMessage } from "../src/models/purpose/purposeEventNotificationMessage.js"; import { toPurposeEventNotification } from "../src/models/purpose/purposeEventNotificationConverter.js"; +import { catalogItemDescriptorUpdatedNotification } from "./resources/catalogItemDescriptorUpdate.js"; import { TEST_ELASTIC_MQ_PORT, elasticMQContainer } from "./utils.js"; -/* const getDescriptorMock = (descriptorId: string): EServiceDescriptorV2 => toDescriptorV2({ id: unsafeBrandId(descriptorId), @@ -88,7 +101,6 @@ const getMockEService = (id: string): EServiceV2 => mode: eserviceMode.deliver, riskAnalysis: [], }); -*/ describe("Notification tests", async () => { process.env.AWS_CONFIG_FILE = "aws.config.local"; @@ -166,7 +178,7 @@ describe("Notification tests", async () => { }); */ describe("Purpose Event Message", async () => { - it("should send a message to the queue", async () => { + it.only("should send a message to the queue", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From a6cb380235108cc2e3c2a7d6dea5acf7b1f78e21 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 10:54:05 +0200 Subject: [PATCH 432/537] Revert "Test" This reverts commit cd7b8a130ba15d3ad4b6a6bfa943478a2262fd02. --- .../test/notificationMessage.integration.test.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 74cc296e17..96d5e19da3 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -126,9 +126,9 @@ describe("Notification tests", async () => { afterAll(async () => { await startedElasticMQContainer.stop(); }); - /* + describe("Update Descriptor Event Message", async () => { - it("should send a message to the queue", async () => { + it.only("should send a message to the queue", async () => { const descriptor = getDescriptorMock( "6b48e234-aac6-4d33-aef4-93816588ff41" ); @@ -176,9 +176,8 @@ describe("Notification tests", async () => { ); }); }); - */ describe("Purpose Event Message", async () => { - it.only("should send a message to the queue", async () => { + it("should send a message to the queue", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From 2d9e528c1d32cc4034e187e521fb85ff9d249a38 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 11:03:23 +0200 Subject: [PATCH 433/537] Test --- .../test/notificationMessage.integration.test.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 96d5e19da3..964edca00a 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -128,7 +128,7 @@ describe("Notification tests", async () => { }); describe("Update Descriptor Event Message", async () => { - it.only("should send a message to the queue", async () => { + it("should send a message to the queue", async () => { const descriptor = getDescriptorMock( "6b48e234-aac6-4d33-aef4-93816588ff41" ); @@ -165,15 +165,9 @@ describe("Notification tests", async () => { await queueWriter.send(message); - const receivedMessage2 = (await queueWriter.receiveLast())[0]; - expect(receivedMessage.payload).toEqual( catalogItemDescriptorUpdatedNotification.payload ); - - expect(receivedMessage2.payload).toEqual( - catalogItemDescriptorUpdatedNotification.payload - ); }); }); describe("Purpose Event Message", async () => { From 235ef7b60eab10487afdb3df1c6fa462721db31b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 11:04:39 +0200 Subject: [PATCH 434/537] Fix --- .../test/notificationMessage.integration.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 964edca00a..269711368e 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -163,8 +163,6 @@ describe("Notification tests", async () => { const receivedMessage = (await queueWriter.receiveLast())[0]; - await queueWriter.send(message); - expect(receivedMessage.payload).toEqual( catalogItemDescriptorUpdatedNotification.payload ); From 2a3071f88b2a585919363383e1e585be4994b688 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 12:02:50 +0200 Subject: [PATCH 435/537] Test --- .../test/notificationMessage.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 269711368e..4036fd8efc 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -180,7 +180,7 @@ describe("Notification tests", async () => { }; const eventEnvelope: PurposeEventEnvelopeV2 = { - sequence_num: 1, + sequence_num: 2, stream_id: mockPurpose.id, version: 1, correlation_id: v4(), From 4787dffdf28cba9620ade9392b7749bf4d469ce7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 12:40:29 +0200 Subject: [PATCH 436/537] Test --- .../notificationMessage.integration.test.ts | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 4036fd8efc..b6cf5bfcb2 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -129,6 +129,9 @@ describe("Notification tests", async () => { describe("Update Descriptor Event Message", async () => { it("should send a message to the queue", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + const descriptor = getDescriptorMock( "6b48e234-aac6-4d33-aef4-93816588ff41" ); @@ -137,12 +140,12 @@ describe("Notification tests", async () => { descriptors: [descriptor], }; - const eventV2: EServiceDescriptorSuspendedV2 = { + const catalogEventV2: EServiceDescriptorSuspendedV2 = { descriptorId: "6b48e234-aac6-4d33-aef4-93816588ff41", eservice: mockEService, }; - const eventEnvelope: EServiceEventEnvelopeV2 = { + const catalogEventEnvelope: EServiceEventEnvelopeV2 = { sequence_num: 1, stream_id: "d27f668f-630b-4889-a97f-2b7e39b24188", version: 1, @@ -150,36 +153,30 @@ describe("Notification tests", async () => { log_date: new Date(), event_version: 2, type: "EServiceDescriptorSuspended", - data: eventV2, + data: catalogEventV2, }; const CatalogItemEventNotification = - toCatalogItemEventNotification(eventEnvelope); + toCatalogItemEventNotification(catalogEventEnvelope); - const message = buildCatalogMessage( - eventEnvelope, + const catalogMessage = buildCatalogMessage( + catalogEventEnvelope, CatalogItemEventNotification ); - await queueWriter.send(message); + await queueWriter.send(catalogMessage); - const receivedMessage = (await queueWriter.receiveLast())[0]; + const receivedCatalogMessage = (await queueWriter.receiveLast())[0]; - expect(receivedMessage.payload).toEqual( + expect(receivedCatalogMessage.payload).toEqual( catalogItemDescriptorUpdatedNotification.payload ); - }); - }); - describe("Purpose Event Message", async () => { - it("should send a message to the queue", async () => { - vi.useFakeTimers(); - vi.setSystemTime(new Date()); const mockPurpose = getMockPurpose(); - const eventV2: PurposeAddedV2 = { + const purposeEventV2: PurposeAddedV2 = { purpose: toPurposeV2(mockPurpose), }; - const eventEnvelope: PurposeEventEnvelopeV2 = { + const purpsoeEventEnvelope: PurposeEventEnvelopeV2 = { sequence_num: 2, stream_id: mockPurpose.id, version: 1, @@ -187,16 +184,16 @@ describe("Notification tests", async () => { log_date: new Date(), event_version: 2, type: "PurposeAdded", - data: eventV2, + data: purposeEventV2, }; const purposeEventNotification = - toPurposeEventNotification(eventEnvelope); + toPurposeEventNotification(purpsoeEventEnvelope); - const message = buildPurposeMessage( - eventEnvelope, + const purposeMessage = buildPurposeMessage( + purpsoeEventEnvelope, purposeEventNotification ); - await queueWriter.send(message); + await queueWriter.send(purposeMessage); const receivedMessage = (await queueWriter.receiveLast())[0]; From 647b7fbe612c6524a68c754970db0452c5d39fe8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 12:41:18 +0200 Subject: [PATCH 437/537] Fix typo --- .../test/notificationMessage.integration.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index b6cf5bfcb2..57cfd34029 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -176,7 +176,7 @@ describe("Notification tests", async () => { purpose: toPurposeV2(mockPurpose), }; - const purpsoeEventEnvelope: PurposeEventEnvelopeV2 = { + const purposeEventEnvelope: PurposeEventEnvelopeV2 = { sequence_num: 2, stream_id: mockPurpose.id, version: 1, @@ -187,10 +187,10 @@ describe("Notification tests", async () => { data: purposeEventV2, }; const purposeEventNotification = - toPurposeEventNotification(purpsoeEventEnvelope); + toPurposeEventNotification(purposeEventEnvelope); const purposeMessage = buildPurposeMessage( - purpsoeEventEnvelope, + purposeEventEnvelope, purposeEventNotification ); await queueWriter.send(purposeMessage); From bf76c0a85b8f9fd3117231e96bc1ab897c2e9b89 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 12:45:47 +0200 Subject: [PATCH 438/537] Test --- .../test/notificationMessage.integration.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 57cfd34029..ad370b7b83 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -164,12 +164,6 @@ describe("Notification tests", async () => { ); await queueWriter.send(catalogMessage); - const receivedCatalogMessage = (await queueWriter.receiveLast())[0]; - - expect(receivedCatalogMessage.payload).toEqual( - catalogItemDescriptorUpdatedNotification.payload - ); - const mockPurpose = getMockPurpose(); const purposeEventV2: PurposeAddedV2 = { @@ -193,11 +187,17 @@ describe("Notification tests", async () => { purposeEventEnvelope, purposeEventNotification ); + await queueWriter.send(purposeMessage); - const receivedMessage = (await queueWriter.receiveLast())[0]; + const receivedMessages = await queueWriter.receiveLast(2); + const receivedCatalogMessage = receivedMessages[0]; + const receivedPurposeMessage = receivedMessages[1]; - expect(receivedMessage.payload).toEqual({ + expect(receivedCatalogMessage.payload).toEqual( + catalogItemDescriptorUpdatedNotification.payload + ); + expect(receivedPurposeMessage.payload).toEqual({ purpose: { ...mockPurpose, createdAt: new Date().toISOString(), From 281827c7836d456ecc147d83105a18007d72facf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 14:04:43 +0200 Subject: [PATCH 439/537] Improvement --- .../test/notificationMessage.integration.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index ad370b7b83..9e8bc7b18e 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -1,7 +1,7 @@ /* eslint-disable functional/immutable-data */ /* eslint-disable functional/no-let */ import { StartedTestContainer } from "testcontainers"; -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, assert, beforeAll, describe, expect, it, vi } from "vitest"; import { EServiceDescriptorSuspendedV2, @@ -127,7 +127,7 @@ describe("Notification tests", async () => { await startedElasticMQContainer.stop(); }); - describe("Update Descriptor Event Message", async () => { + describe("Catalog and Purpose Event Message", async () => { it("should send a message to the queue", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); @@ -191,6 +191,8 @@ describe("Notification tests", async () => { await queueWriter.send(purposeMessage); const receivedMessages = await queueWriter.receiveLast(2); + expect(receivedMessages.length).toBe(2); + const receivedCatalogMessage = receivedMessages[0]; const receivedPurposeMessage = receivedMessages[1]; From 4b56c5459c1f750001d106743968afb71cfd17d4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 16:16:20 +0200 Subject: [PATCH 440/537] Remove unused function --- packages/commons-test/src/testUtils.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 73997a3574..4d6e87ba0b 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -177,16 +177,6 @@ export const getMockPurposeVersion = (): PurposeVersion => ({ createdAt: new Date(), }); -export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ - organizationId: organizationId || generateId(), - userId: uuidv4(), - userRoles: [], - externalId: { - value: "123456", - origin: "IPA", - }, -}); - export const getMockPurposeVersionDocument = (): PurposeVersionDocument => ({ path: "path", id: generateId(), From 13c9adb39687f6181858874dc3ca745f623ac1f5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 16:19:11 +0200 Subject: [PATCH 441/537] Fix import --- packages/purpose-process/src/model/domain/errors.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a02d522bb0..5031b2fe15 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -7,7 +7,6 @@ import { TenantId, makeApiProblemBuilder, } from "pagopa-interop-models"; -import { logger } from "pagopa-interop-commons"; export const errorCodes = { purposeNotFound: "0001", From e4c72b82a173dc235043c6ca29f76bfc8477353e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 16:22:56 +0200 Subject: [PATCH 442/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 5 ++++- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index de4cd412dc..6125eb1b93 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -132,18 +132,21 @@ const purposeRouter = ( "/purposes/:purposeId/versions/:versionId", authorizationMiddleware([ADMIN_ROLE, INTERNAL_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { await purposeService.deletePurposeVersion({ purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, + logger: ctx.logger, }); return res.status(204).end(); } catch (error) { const errorRes = makeApiProblem( error, - deletePurposeVersionErrorMapper + deletePurposeVersionErrorMapper, + ctx.logger ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b9112e6234..df60993e0f 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -1,4 +1,4 @@ -import { DB, Logger } from "pagopa-interop-commons"; +import { DB, Logger, eventRepository } from "pagopa-interop-commons"; import { EService, EServiceId, @@ -170,7 +170,7 @@ export function purposeServiceBuilder( versionId, organizationId, correlationId, - logger + logger, }: { purposeId: PurposeId; versionId: PurposeVersionId; From de2b92320c71582f4c0b71c1b4ef44c5694ffbc2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 16:23:43 +0200 Subject: [PATCH 443/537] Fix test --- .../purpose-process/test/testGetRiskAnalysisDocument.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts index b34be4928c..de82c8383b 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts @@ -15,6 +15,7 @@ import { TenantId, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, purposeVersionNotFound, @@ -53,6 +54,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: mockPurposeVersion.id, documentId: mockDocument.id, organizationId: mockEService.producerId, + logger: genericLogger, }); expect(result).toEqual(mockDocument); }); @@ -66,6 +68,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: generateId(), documentId: generateId(), organizationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); @@ -93,6 +96,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: randomVersionId, documentId: randomDocumentId, organizationId: mockEService.producerId, + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) @@ -121,6 +125,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: mockPurposeVersion.id, documentId: randomDocumentId, organizationId: mockEService.producerId, + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionDocumentNotFound( @@ -153,6 +158,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: mockPurposeVersion.id, documentId: mockDocument.id, organizationId: randomId, + logger: genericLogger, }) ).rejects.toThrowError(organizationNotAllowed(randomId)); }); From 763a2d7e347566f9eef075ff9b269856762afa9f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 16:25:50 +0200 Subject: [PATCH 444/537] Fix test --- .../purpose-process/test/testGetRiskAnalysisDocument.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts index b34be4928c..de82c8383b 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisDocument.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisDocument.ts @@ -15,6 +15,7 @@ import { TenantId, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, purposeVersionNotFound, @@ -53,6 +54,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: mockPurposeVersion.id, documentId: mockDocument.id, organizationId: mockEService.producerId, + logger: genericLogger, }); expect(result).toEqual(mockDocument); }); @@ -66,6 +68,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: generateId(), documentId: generateId(), organizationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(notExistingId)); }); @@ -93,6 +96,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: randomVersionId, documentId: randomDocumentId, organizationId: mockEService.producerId, + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) @@ -121,6 +125,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: mockPurposeVersion.id, documentId: randomDocumentId, organizationId: mockEService.producerId, + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionDocumentNotFound( @@ -153,6 +158,7 @@ export const testGetRiskAnalysisDocument = (): ReturnType => versionId: mockPurposeVersion.id, documentId: mockDocument.id, organizationId: randomId, + logger: genericLogger, }) ).rejects.toThrowError(organizationNotAllowed(randomId)); }); From e1a234f7dbc5c03aed405eebedff8ce7784b3e23 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 May 2024 16:26:50 +0200 Subject: [PATCH 445/537] Fix test --- packages/purpose-process/test/testDeletePurposeVersion.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index b91e8ed35f..306332fcd4 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -17,6 +17,7 @@ import { PurposeId, PurposeVersionId, } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, purposeVersionNotFound, @@ -58,6 +59,7 @@ export const testDeletePurposeVersion = (): ReturnType => versionId: mockPurposeVersion1.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -107,6 +109,7 @@ export const testDeletePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(randomId)); }); @@ -128,6 +131,7 @@ export const testDeletePurposeVersion = (): ReturnType => versionId: randomVersionId, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) @@ -154,6 +158,7 @@ export const testDeletePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( organizationIsNotTheConsumer(mockEService.producerId) @@ -186,6 +191,7 @@ export const testDeletePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) @@ -212,6 +218,7 @@ export const testDeletePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionCannotBeDeleted(mockPurpose.id, mockPurposeVersion.id) From 4f25a09995700f8a40a25e8cec98f21216e11a02 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 16:36:02 +0200 Subject: [PATCH 446/537] minor fix --- packages/purpose-process/src/model/domain/errors.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index b88d566ed9..d31a4d7502 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -246,7 +246,7 @@ export function purposeCannotBeCloned( }); } -export function RiskAnalysisConfigVersionNotFound( +export function riskAnalysisConfigVersionNotFound( version: string, tenantKind: TenantKind ): ApiError { diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1077234454..dde5758747 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -55,7 +55,7 @@ import { purposeVersionDocumentNotFound, purposeVersionNotFound, tenantNotFound, - RiskAnalysisConfigVersionNotFound, + riskAnalysisConfigVersionNotFound, } from "../model/domain/errors.js"; import { toCreateEventDraftPurposeDeleted, @@ -868,7 +868,7 @@ export function purposeServiceBuilder( ); if (!riskAnalysisFormConfig) { - throw RiskAnalysisConfigVersionNotFound( + throw riskAnalysisConfigVersionNotFound( riskAnalysisVersion, tenant.kind ); From 18e43a3a376e5d3304c9bd15887a273502e4804c Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 16:38:14 +0200 Subject: [PATCH 447/537] implemented retrieveLatestRiskAnalysis --- .../test/getFormRules.unit.test.ts | 18 +++- .../risk-analysis/riskAnalysisValidation.ts | 2 +- .../src/model/domain/errors.ts | 11 +++ .../src/routers/PurposeRouter.ts | 25 +++++- .../src/services/purposeService.ts | 26 ++++++ .../src/utilities/errorMappers.ts | 7 ++ .../test/purposeService.integration.test.ts | 1 + .../testGetLatestRiskAnalysisConfiguration.ts | 82 +++++++++++++++++++ 8 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts diff --git a/packages/commons-test/test/getFormRules.unit.test.ts b/packages/commons-test/test/getFormRules.unit.test.ts index 3b6aa2360c..43218dc6ad 100644 --- a/packages/commons-test/test/getFormRules.unit.test.ts +++ b/packages/commons-test/test/getFormRules.unit.test.ts @@ -1,4 +1,11 @@ -import { getFormRulesByVersion, pa1, private1 } from "pagopa-interop-commons"; +import { + getFormRulesByVersion, + getLatestVersionFormRules, + pa1, + pa3, + private1, + private2, +} from "pagopa-interop-commons"; import { tenantKind } from "pagopa-interop-models"; import { describe, it, expect } from "vitest"; @@ -17,4 +24,13 @@ describe("Form rules retrieve", () => { expect(getFormRulesByVersion(tenantKind.PA, "0.0")).toBeUndefined(); }); }); + describe("getLatestVersionFormRules", () => { + it.each(Object.values(tenantKind))( + "should retrieve latest form rules for kind %s", + (kind) => { + const riskAnalysisFormConfig = kind === tenantKind.PA ? pa3 : private2; // TO BE UPDATED with latest versions + expect(getLatestVersionFormRules(kind)).toEqual(riskAnalysisFormConfig); + } + ); + }); }); diff --git a/packages/commons/src/risk-analysis/riskAnalysisValidation.ts b/packages/commons/src/risk-analysis/riskAnalysisValidation.ts index 7cbc139ef0..3c6c7af5e1 100644 --- a/packages/commons/src/risk-analysis/riskAnalysisValidation.ts +++ b/packages/commons/src/risk-analysis/riskAnalysisValidation.ts @@ -99,7 +99,7 @@ function getSanitizedAnswers( ); } -function getLatestVersionFormRules( +export function getLatestVersionFormRules( tenantKind: TenantKind ): RiskAnalysisFormRules | undefined { const rules = riskAnalysisFormRules[tenantKind]; diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index b88d566ed9..ea9eddf016 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -36,6 +36,7 @@ export const errorCodes = { eserviceRiskAnalysisNotFound: "0020", purposeCannotBeCloned: "0021", riskAnalysisConfigVersionNotFound: "0022", + riskAnalysisConfigLatestVersionNotFound: "0023", }; export type ErrorCodes = keyof typeof errorCodes; @@ -256,3 +257,13 @@ export function RiskAnalysisConfigVersionNotFound( title: "Risk Analysis config version not found", }); } + +export function riskAnalysisConfigLatestVersionNotFound( + tenantKind: TenantKind +): ApiError { + return new ApiError({ + detail: `Latest Risk Analysis Configuration for tenant kind ${tenantKind} not found`, + code: "riskAnalysisConfigLatestVersionNotFound", + title: "Risk Analysis config latest version not found", + }); +} diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 27f713a27a..c6b88575b4 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -31,6 +31,7 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, + retrieveLatestRiskAnalysisConfigurationErrorMapper, retrieveRiskAnalysisConfigurationByVersionErrorMapper, suspendPurposeVersionErrorMapper, updatePurposeErrorMapper, @@ -405,7 +406,29 @@ const purposeRouter = ( .get( "/purposes/riskAnalysis/latest", authorizationMiddleware([ADMIN_ROLE, SUPPORT_ROLE]), - (_req, res) => res.status(501).send() + async (req, res) => { + try { + const riskAnalysisConfiguration = + await purposeService.retrieveLatestRiskAnalysisConfiguration({ + tenantKind: req.query.tenantKind, + organizationId: req.ctx.authData.organizationId, + }); + return res + .status(200) + .json( + riskAnalysisFormConfigToApiRiskAnalysisFormConfig( + riskAnalysisConfiguration + ) + ) + .end(); + } catch (error) { + const errorRes = makeApiProblem( + error, + retrieveLatestRiskAnalysisConfigurationErrorMapper + ); + return res.status(errorRes.status).json(errorRes).end(); + } + } ) .get( "/purposes/riskAnalysis/version/:riskAnalysisVersion", diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1077234454..1ffcac4d99 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -5,6 +5,7 @@ import { eventRepository, formatDateAndTime, getFormRulesByVersion, + getLatestVersionFormRules, logger, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, @@ -56,6 +57,8 @@ import { purposeVersionNotFound, tenantNotFound, RiskAnalysisConfigVersionNotFound, + tenantKindNotFound, + riskAnalysisConfigLatestVersionNotFound, } from "../model/domain/errors.js"; import { toCreateEventDraftPurposeDeleted, @@ -874,6 +877,29 @@ export function purposeServiceBuilder( ); } + return riskAnalysisFormConfig; + }, + async retrieveLatestRiskAnalysisConfiguration({ + tenantKind, + organizationId, + }: { + tenantKind: TenantKind | undefined; + organizationId: TenantId; + }): Promise { + logger.info(`Retrieve latest risk analysis configuration`); + + const tenant = await retrieveTenant(organizationId, readModelService); + const kind = tenantKind || tenant.kind; + + if (!kind) { + throw tenantKindNotFound(tenant.id); + } + + const riskAnalysisFormConfig = getLatestVersionFormRules(kind); + if (!riskAnalysisFormConfig) { + throw riskAnalysisConfigLatestVersionNotFound(kind); + } + return riskAnalysisFormConfig; }, }; diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index c8bf32f981..b83abbb0cb 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -180,3 +180,10 @@ export const retrieveRiskAnalysisConfigurationByVersionErrorMapper = ( ) .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const retrieveLatestRiskAnalysisConfigurationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 3a4e01af88..d992a67492 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -42,6 +42,7 @@ import { testCreatePurpose } from "./testCreatePurpose.js"; import { testCreateReversePurpose } from "./testCreateReversePurpose.js"; import { testClonePurpose } from "./testClonePurpose.js"; import { testGetRiskAnalysisConfigurationByVersion } from "./testGetRiskAnalysisConfigurationByVersion.js"; +import { testGetLatestRiskAnalysisConfiguration } from "./testGetLatestRiskAnalysisConfiguration.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; diff --git a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts new file mode 100644 index 0000000000..6d76b6c19a --- /dev/null +++ b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts @@ -0,0 +1,82 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + getMockTenant, + writeInReadmodel, +} from "pagopa-interop-commons-test/index.js"; +import { + TenantId, + TenantKind, + generateId, + tenantKind, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { getLatestVersionFormRules } from "pagopa-interop-commons"; +import { + tenantKindNotFound, + tenantNotFound, + riskAnalysisConfigLatestVersionNotFound, +} from "../src/model/domain/errors.js"; +import { purposeService, tenants } from "./purposeService.integration.test.js"; +export const testGetLatestRiskAnalysisConfiguration = (): ReturnType< + typeof describe +> => + describe("retrieveLatestRiskAnalysisConfiguration", async () => { + it.each(Object.values(tenantKind))( + "should retrieve latest risk analysis configuration for kind %s", + async (kind) => { + const mockTenant = { + ...getMockTenant(), + kind, + }; + await writeInReadmodel(mockTenant, tenants); + + const result = + await purposeService.retrieveLatestRiskAnalysisConfiguration({ + tenantKind: kind, + organizationId: mockTenant.id, + }); + + expect(result).toEqual(getLatestVersionFormRules(kind)); + } + ); + it("should throw tenantNotFound if the tenant doesn't exist", async () => { + const randomId = generateId(); + + expect( + purposeService.retrieveLatestRiskAnalysisConfiguration({ + tenantKind: tenantKind.PA, + organizationId: randomId, + }) + ).rejects.toThrowError(tenantNotFound(randomId)); + }); + it("should throw tenantKindNotFound if the tenant kind is undefined", async () => { + const mockTenant = { + ...getMockTenant(), + kind: undefined, + }; + await writeInReadmodel(mockTenant, tenants); + + expect( + purposeService.retrieveLatestRiskAnalysisConfiguration({ + tenantKind: undefined, + organizationId: mockTenant.id, + }) + ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); + }); + it("should throw riskAnalysisConfigLatestVersionNotFound if a config with that version doesn't exist", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + await writeInReadmodel(mockTenant, tenants); + + const kind = "unkown" as TenantKind; + + expect( + purposeService.retrieveLatestRiskAnalysisConfiguration({ + tenantKind: kind, + organizationId: mockTenant.id, + }) + ).rejects.toThrowError(riskAnalysisConfigLatestVersionNotFound(kind)); + }); + }); From 4bd230d08d7f87f420e87e04a171195d9caa237d Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 16:44:05 +0200 Subject: [PATCH 448/537] fix test --- .../test/testGetRiskAnalysisConfigurationByVersion.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts index 1df0d832e9..ba9223d25e 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts @@ -16,7 +16,7 @@ import { import { describe, expect, it } from "vitest"; import { getFormRulesByVersion } from "pagopa-interop-commons"; import { - RiskAnalysisConfigVersionNotFound, + riskAnalysisConfigVersionNotFound, eserviceNotFound, tenantKindNotFound, tenantNotFound, @@ -133,7 +133,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< organizationId: mockTenant.id, }) ).rejects.toThrowError( - RiskAnalysisConfigVersionNotFound( + riskAnalysisConfigVersionNotFound( wrongRiskAnalysisVersion, mockTenant.kind ) From 2b41c0dfb6bbce78ea6867390f522ee503d36bf6 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 16:52:41 +0200 Subject: [PATCH 449/537] missed error in erroMapper --- packages/purpose-process/src/utilities/errorMappers.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index b83abbb0cb..2faf6ab8d8 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -185,5 +185,10 @@ export const retrieveLatestRiskAnalysisConfigurationErrorMapper = ( error: ApiError ): number => match(error.code) - .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with( + "tenantNotFound", + "tenantKindNotFound", + "riskAnalysisConfigLatestVersionNotFound", + () => HTTP_STATUS_BAD_REQUEST + ) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From 8c6de7b26e42cf5f598ede66aff2ae0f22e93767 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 16:54:12 +0200 Subject: [PATCH 450/537] missed test's call --- packages/purpose-process/test/purposeService.integration.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index d992a67492..f5067f9179 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -111,5 +111,6 @@ describe("Integration tests", async () => { testCreateReversePurpose(); testClonePurpose(); testGetRiskAnalysisConfigurationByVersion(); + testGetLatestRiskAnalysisConfiguration(); }); }); From 58fd048eb389ac7a53e4c54b0a027fa24b69bc60 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 16:55:16 +0200 Subject: [PATCH 451/537] fix test --- .../test/testGetLatestRiskAnalysisConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts index 6d76b6c19a..09b7631ada 100644 --- a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts +++ b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts @@ -63,7 +63,7 @@ export const testGetLatestRiskAnalysisConfiguration = (): ReturnType< }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); - it("should throw riskAnalysisConfigLatestVersionNotFound if a config with that version doesn't exist", async () => { + it("should throw riskAnalysisConfigLatestVersionNotFound if a config with that kind doesn't exist", async () => { const mockTenant = { ...getMockTenant(), kind: tenantKind.PA, From 72b8bfdaee9ccf4bfe6a39d74c50790eb67a792e Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 17:28:45 +0200 Subject: [PATCH 452/537] Apply suggestions from code review Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- packages/purpose-process/src/services/readModelService.ts | 2 +- packages/purpose-process/src/services/validators.ts | 2 +- packages/purpose-process/src/utilities/errorMappers.ts | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index ac744cc537..a577de08c1 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -109,7 +109,7 @@ export function readModelServiceBuilder( ): Promise | undefined> { return getPurpose(purposes, { "data.id": id }); }, - async getSpecificPurpose( + async getPurpose( eserviceId: EServiceId, consumerId: TenantId, title: string diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index e13495dd66..634d20b2c2 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -57,7 +57,7 @@ export const isDeletableVersion = ( export const isRejectable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.waitingForApproval; -export const assertEserviceHasSpecificMode = ( +export const assertEserviceMode = ( eservice: EService, expectedMode: EServiceMode ): void => { diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index a4377747ba..20c62990a2 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -66,10 +66,7 @@ export const updatePurposeErrorMapper = (error: ApiError): number => .with( "eServiceModeNotAllowed", "missingFreeOfChargeReason", - "tenantKindNotFound", "riskAnalysisValidationFailed", - "eserviceNotFound", - "tenantNotFound", () => HTTP_STATUS_BAD_REQUEST ) .with( From 6f5ae17076f6e17199923471106369a5b9d0ff29 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 18:04:55 +0200 Subject: [PATCH 453/537] applied suggestions from review and update tests --- .../src/routers/PurposeRouter.ts | 3 +- .../src/services/purposeService.ts | 34 ++-- .../src/utilities/errorMappers.ts | 2 + .../test/purposeService.integration.test.ts | 8 +- .../purpose-process/test/testUpdatePurpose.ts | 159 ++++++++++-------- 5 files changed, 116 insertions(+), 90 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 70c6c4f217..1adf8aa3cf 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -26,6 +26,7 @@ import { getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, updatePurposeErrorMapper, + updateReversePurposeErrorMapper, } from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( @@ -101,7 +102,7 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - updatePurposeErrorMapper, + updateReversePurposeErrorMapper, ctx.logger ); return res.status(errorRes.status).json(errorRes).end(); diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index daf9dd9c94..b4b9885d1b 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -52,7 +52,7 @@ import { import { ReadModelService } from "./readModelService.js"; import { assertOrganizationIsAConsumer, - assertEserviceHasSpecificMode, + assertEserviceMode, assertConsistentFreeOfCharge, isRiskAnalysisFormValid, isDeletableVersion, @@ -305,12 +305,14 @@ export function purposeServiceBuilder( logger: Logger; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info(`Updating Purpose ${purposeId}`); - return await updatePurposeInternal( + return await performUpdatePurpose( purposeId, purposeUpdateContent, organizationId, eserviceMode.deliver, - { readModelService, correlationId, repository } + readModelService, + correlationId, + repository ); }, async updateReversePurpose({ @@ -327,12 +329,14 @@ export function purposeServiceBuilder( logger: Logger; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info(`Updating Reverse Purpose ${purposeId}`); - return await updatePurposeInternal( + return await performUpdatePurpose( purposeId, reversePurposeUpdateContent, organizationId, eserviceMode.receive, - { readModelService, correlationId, repository } + readModelService, + correlationId, + repository ); }, }; @@ -417,21 +421,15 @@ const getInvolvedTenantByEServiceMode = async ( } }; -const updatePurposeInternal = async ( +const performUpdatePurpose = async ( purposeId: PurposeId, updateContent: ApiPurposeUpdateContent | ApiReversePurposeUpdateContent, organizationId: TenantId, mode: EServiceMode, - { - readModelService, - correlationId, - repository, - }: { - readModelService: ReadModelService; - correlationId: string; - repository: { - createEvent: (createEvent: CreateEvent) => Promise; - }; + readModelService: ReadModelService, + correlationId: string, + repository: { + createEvent: (createEvent: CreateEvent) => Promise; } ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { const purpose = await retrievePurpose(purposeId, readModelService); @@ -439,7 +437,7 @@ const updatePurposeInternal = async ( assertPurposeIsDraft(purpose.data); if (updateContent.title !== purpose.data.title) { - const purposeWithSameTitle = await readModelService.getSpecificPurpose( + const purposeWithSameTitle = await readModelService.getPurpose( purpose.data.eserviceId, purpose.data.consumerId, updateContent.title @@ -453,7 +451,7 @@ const updatePurposeInternal = async ( purpose.data.eserviceId, readModelService ); - assertEserviceHasSpecificMode(eservice, mode); + assertEserviceMode(eservice, mode); assertConsistentFreeOfCharge( updateContent.isFreeOfCharge, updateContent.freeOfChargeReason diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 20c62990a2..4976147341 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -77,3 +77,5 @@ export const updatePurposeErrorMapper = (error: ApiError): number => .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const updateReversePurposeErrorMapper = updatePurposeErrorMapper; diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 2738593e13..69a27ec8ee 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -85,10 +85,10 @@ describe("Integration tests", async () => { }); describe("Purpose service", () => { - testGetPurposeById(); - testGetRiskAnalysisDocument(); - testDeletePurposeVersion(); - testRejectPurposeVersion(); + // testGetPurposeById(); + // testGetRiskAnalysisDocument(); + // testDeletePurposeVersion(); + // testRejectPurposeVersion(); testUpdatePurpose(); }); }); diff --git a/packages/purpose-process/test/testUpdatePurpose.ts b/packages/purpose-process/test/testUpdatePurpose.ts index 8e1950c6c6..4b1c1b9a6c 100644 --- a/packages/purpose-process/test/testUpdatePurpose.ts +++ b/packages/purpose-process/test/testUpdatePurpose.ts @@ -1,6 +1,10 @@ +/* eslint-disable functional/no-let */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { unexpectedRulesVersionError } from "pagopa-interop-commons"; +import { + genericLogger, + unexpectedRulesVersionError, +} from "pagopa-interop-commons"; import { randomArrayItem, getMockTenant, @@ -29,7 +33,7 @@ import { RiskAnalysis, eserviceMode, } from "pagopa-interop-models"; -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import { purposeNotFound, organizationIsNotTheConsumer, @@ -63,63 +67,77 @@ import { export const testUpdatePurpose = (): ReturnType => describe("updatePurpose and updateReversePurpose", () => { const tenantType = randomArrayItem(Object.values(tenantKind)); - const tenant: Tenant = { - ...getMockTenant(), - kind: tenantType, - }; - - const eServiceDeliver: EService = { - ...getMockEService(), - mode: eserviceMode.deliver, - }; - - const eServiceReceive: EService = { - ...getMockEService(), - mode: eserviceMode.receive, - producerId: tenant.id, - }; - - const purposeForReceive: Purpose = { - ...getMockPurpose(), - eserviceId: eServiceReceive.id, - consumerId: tenant.id, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.draft }, - ], - riskAnalysisForm: { - ...getMockValidRiskAnalysisForm(tenantType), - id: generateId(), - }, - }; - - const purposeForDeliver: Purpose = { - ...getMockPurpose(), - eserviceId: eServiceDeliver.id, - consumerId: tenant.id, - versions: [ - { ...getMockPurposeVersion(), state: purposeVersionState.draft }, - ], - }; - - const validRiskAnalysis = getMockValidRiskAnalysis(tenantType); - - const purposeUpdateContent: ApiPurposeUpdateContent = { - title: "test", - dailyCalls: 10, - description: "test", - isFreeOfCharge: true, - freeOfChargeReason: "reason", - riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), - }; - - const reversePurposeUpdateContent: ApiReversePurposeUpdateContent = { - ...purposeUpdateContent, - }; - - it("Should write on event store for the update of a purpose of an e-service in mode DELIVER (including title change)", async () => { + let tenant: Tenant; + let eServiceDeliver: EService; + let eServiceReceive: EService; + let purposeForReceive: Purpose; + let purposeForDeliver: Purpose; + let validRiskAnalysis: RiskAnalysis; + let purposeUpdateContent: ApiPurposeUpdateContent; + let reversePurposeUpdateContent: ApiReversePurposeUpdateContent; + + beforeAll(() => { vi.useFakeTimers(); vi.setSystemTime(new Date()); + tenant = { + ...getMockTenant(), + kind: tenantType, + }; + + eServiceDeliver = { + ...getMockEService(), + mode: eserviceMode.deliver, + }; + + eServiceReceive = { + ...getMockEService(), + mode: eserviceMode.receive, + producerId: tenant.id, + }; + + purposeForReceive = { + ...getMockPurpose(), + eserviceId: eServiceReceive.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + riskAnalysisForm: { + ...getMockValidRiskAnalysisForm(tenantType), + id: generateId(), + }, + }; + + purposeForDeliver = { + ...getMockPurpose(), + eserviceId: eServiceDeliver.id, + consumerId: tenant.id, + versions: [ + { ...getMockPurposeVersion(), state: purposeVersionState.draft }, + ], + }; + + validRiskAnalysis = getMockValidRiskAnalysis(tenantType); + + purposeUpdateContent = { + title: "test", + dailyCalls: 10, + description: "test", + isFreeOfCharge: false, + riskAnalysisForm: buildRiskAnalysisSeed(validRiskAnalysis), + }; + + reversePurposeUpdateContent = { + ...purposeUpdateContent, + }; + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("Should write on event store for the update of a purpose of an e-service in mode DELIVER (including title change)", async () => { await addOnePurpose(purposeForDeliver, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(tenant, tenants); @@ -129,6 +147,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -157,13 +176,9 @@ export const testUpdatePurpose = (): ReturnType => ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - vi.useRealTimers(); }); it("Should write on event store for the update of a purpose of an e-service in mode DELIVER (no title change)", async () => { - vi.useFakeTimers(); - vi.setSystemTime(new Date()); - await addOnePurpose(purposeForDeliver, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(tenant, tenants); @@ -178,6 +193,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent: updateContentWithoutTitle, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -206,12 +222,8 @@ export const testUpdatePurpose = (): ReturnType => ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - vi.useRealTimers(); }); it("Should write on event store for the update of a purpose of an e-service in mode RECEIVE (including title change)", async () => { - vi.useFakeTimers(); - vi.setSystemTime(new Date()); - await addOnePurpose(purposeForReceive, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceReceive), eservices); await writeInReadmodel(tenant, tenants); @@ -221,6 +233,7 @@ export const testUpdatePurpose = (): ReturnType => reversePurposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -249,7 +262,6 @@ export const testUpdatePurpose = (): ReturnType => ); expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - vi.useRealTimers(); }); it("Should throw purposeNotFound if the purpose doesn't exist", async () => { await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); @@ -263,6 +275,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(purposeId)); }); @@ -284,6 +297,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(organizationIsNotTheConsumer(organizationId)); }); @@ -309,6 +323,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotInDraftState(mockPurpose.id)); } @@ -331,6 +346,7 @@ export const testUpdatePurpose = (): ReturnType => }, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( duplicatedPurposeTitle(purposeWithDuplicatedTitle.title) @@ -352,6 +368,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(eserviceNotFound(eserviceId)); }); @@ -366,6 +383,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( eServiceModeNotAllowed(eServiceReceive.id, "Deliver") @@ -382,12 +400,13 @@ export const testUpdatePurpose = (): ReturnType => reversePurposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( eServiceModeNotAllowed(eServiceDeliver.id, "Receive") ); }); - it("Should throw missingFreeOfChargeReason if the freeOfChargeReason is missing", async () => { + it("Should throw missingFreeOfChargeReason if isFreeOfCharge is true but freeOfChargeReason is missing", async () => { await addOnePurpose(purposeForDeliver, postgresDB, purposes); await writeInReadmodel(toReadModelEService(eServiceDeliver), eservices); await writeInReadmodel(tenant, tenants); @@ -397,10 +416,11 @@ export const testUpdatePurpose = (): ReturnType => purposeId: purposeForDeliver.id, purposeUpdateContent: { ...purposeUpdateContent, - freeOfChargeReason: undefined, + isFreeOfCharge: true, }, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(missingFreeOfChargeReason()); }); @@ -414,6 +434,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(tenantNotFound(tenant.id)); @@ -426,6 +447,7 @@ export const testUpdatePurpose = (): ReturnType => reversePurposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); @@ -445,6 +467,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent, organizationId: mockTenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); @@ -472,6 +495,7 @@ export const testUpdatePurpose = (): ReturnType => purposeUpdateContent: mockPurposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) @@ -497,6 +521,7 @@ export const testUpdatePurpose = (): ReturnType => reversePurposeUpdateContent, organizationId: tenant.id, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( riskAnalysisValidationFailed([unexpectedRulesVersionError("0")]) From 40b2e4d7e31d60fd0a923d688b17f4d1ed75c657 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 18:18:44 +0200 Subject: [PATCH 454/537] applied suggestions --- .../src/services/purposeService.ts | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b4b9885d1b..093f81c5e0 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -23,7 +23,6 @@ import { purposeVersionState, PurposeRiskAnalysisForm, PurposeEvent, - EServiceMode, eserviceMode, } from "pagopa-interop-models"; import { @@ -307,9 +306,11 @@ export function purposeServiceBuilder( logger.info(`Updating Purpose ${purposeId}`); return await performUpdatePurpose( purposeId, - purposeUpdateContent, + { + updateContent: purposeUpdateContent, + mode: eserviceMode.deliver, + }, organizationId, - eserviceMode.deliver, readModelService, correlationId, repository @@ -331,9 +332,11 @@ export function purposeServiceBuilder( logger.info(`Updating Reverse Purpose ${purposeId}`); return await performUpdatePurpose( purposeId, - reversePurposeUpdateContent, + { + updateContent: reversePurposeUpdateContent, + mode: eserviceMode.receive, + }, organizationId, - eserviceMode.receive, readModelService, correlationId, repository @@ -423,14 +426,19 @@ const getInvolvedTenantByEServiceMode = async ( const performUpdatePurpose = async ( purposeId: PurposeId, - updateContent: ApiPurposeUpdateContent | ApiReversePurposeUpdateContent, + { + mode, + updateContent, + }: + | { mode: "Deliver"; updateContent: ApiPurposeUpdateContent } + | { mode: "Receive"; updateContent: ApiReversePurposeUpdateContent }, organizationId: TenantId, - mode: EServiceMode, readModelService: ReadModelService, correlationId: string, repository: { createEvent: (createEvent: CreateEvent) => Promise; } + // eslint-disable-next-line max-params ): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> => { const purpose = await retrievePurpose(purposeId, readModelService); assertOrganizationIsAConsumer(organizationId, purpose.data.consumerId); @@ -468,7 +476,7 @@ const performUpdatePurpose = async ( const newRiskAnalysis: PurposeRiskAnalysisForm | undefined = mode === eserviceMode.deliver ? validateAndTransformRiskAnalysis( - (updateContent as ApiPurposeUpdateContent).riskAnalysisForm, + updateContent.riskAnalysisForm, tenant.kind ) : reverseValidateAndTransformRiskAnalysis( @@ -478,7 +486,10 @@ const performUpdatePurpose = async ( const updatedPurpose: Purpose = { ...purpose.data, - ...updateContent, + title: updateContent.title, + description: updateContent.description, + isFreeOfCharge: updateContent.isFreeOfCharge, + freeOfChargeReason: updateContent.freeOfChargeReason, versions: [ { ...purpose.data.versions[0], From d7a8ee71ef6dad07333d6f0ca01da8cc18c74c9a Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 8 May 2024 18:52:43 +0200 Subject: [PATCH 455/537] fix tests --- .../test/purposeService.integration.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index 69a27ec8ee..2738593e13 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -85,10 +85,10 @@ describe("Integration tests", async () => { }); describe("Purpose service", () => { - // testGetPurposeById(); - // testGetRiskAnalysisDocument(); - // testDeletePurposeVersion(); - // testRejectPurposeVersion(); + testGetPurposeById(); + testGetRiskAnalysisDocument(); + testDeletePurposeVersion(); + testRejectPurposeVersion(); testUpdatePurpose(); }); }); From f1aede4d56f4e7fa4e8c0e9a5b3776d473b1f626 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:12:32 +0200 Subject: [PATCH 456/537] Refactor --- packages/notifier-seeder/src/index.ts | 98 ++++++++++----------------- 1 file changed, 37 insertions(+), 61 deletions(-) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index d9cf590ee5..471b8d3cd3 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable functional/immutable-data */ import { runConsumer } from "kafka-iam-auth"; import { EachMessagePayload } from "kafkajs"; @@ -13,7 +14,7 @@ import { runWithContext, } from "pagopa-interop-commons"; import { match } from "ts-pattern"; -import { EServiceEvent, PurposeEventV2 } from "pagopa-interop-models"; +import { EServiceEventV2, PurposeEventV2 } from "pagopa-interop-models"; import { toCatalogItemEventNotification } from "./models/catalog/catalogItemEventNotificationConverter.js"; import { buildCatalogMessage } from "./models/catalog/catalogItemEventNotificationMessage.js"; import { initQueueManager } from "./queue-manager/queueManager.js"; @@ -37,80 +38,55 @@ export function processMessage( purposeTopic: PurposeTopicConfig ) { return async (kafkaMessage: EachMessagePayload): Promise => { - await match(kafkaMessage.topic) - .with(catalogTopic.catalogTopic, async () => { + const { message, decodedMessage } = match(kafkaMessage.topic) + .with(catalogTopic.catalogTopic, () => { const decodedMessage = decodeKafkaMessage( kafkaMessage.message, - EServiceEvent + EServiceEventV2 ); - await runWithContext( - { - messageData: { - eventType: decodedMessage.type, - eventVersion: decodedMessage.event_version, - streamId: decodedMessage.stream_id, - }, - correlationId: decodedMessage.correlation_id, - }, - async () => { - if (decodedMessage.event_version !== 2) { - logger.info( - `Event with version ${decodedMessage.event_version} skipped` - ); - return; - } - - const eserviceV1Event = - toCatalogItemEventNotification(decodedMessage); - const message = buildCatalogMessage( - decodedMessage, - eserviceV1Event - ); - await queueManager.send(message); - - logger.info( - `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` - ); - } - ); + const event = toCatalogItemEventNotification(decodedMessage); + const message = buildCatalogMessage(decodedMessage, event); + return { decodedMessage, message }; }) - .with(purposeTopic.purposeTopic, async () => { + .with(purposeTopic.purposeTopic, () => { const decodedMessage = decodeKafkaMessage( kafkaMessage.message, PurposeEventV2 ); - await runWithContext( - { - messageData: { - eventType: decodedMessage.type, - eventVersion: decodedMessage.event_version, - streamId: decodedMessage.stream_id, - }, - correlationId: decodedMessage.correlation_id, - }, - async () => { - if (decodedMessage.event_version !== 2) { - logger.info( - `Event with version ${decodedMessage.event_version} skipped` - ); - return; - } - - const purposeV1Event = toPurposeEventNotification(decodedMessage); - const message = buildPurposeMessage(decodedMessage, purposeV1Event); - await queueManager.send(message); - - logger.info( - `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` - ); - } - ); + const event = toPurposeEventNotification(decodedMessage); + const message = buildPurposeMessage(decodedMessage, event); + return { decodedMessage, message }; }) .otherwise(() => { throw new Error(`Unknown topic: ${kafkaMessage.topic}`); }); + + await runWithContext( + { + messageData: { + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + }, + correlationId: decodedMessage.correlation_id, + }, + async () => { + if (decodedMessage.event_version !== 2) { + logger.info( + `Event with version ${decodedMessage.event_version} skipped` + ); + return; + } + + await queueManager.send(message); + + logger.info( + `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` + ); + } + ); }; } From d13c2f9cee869faa9bbfe36055593fe7a9329d62 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:31:28 +0200 Subject: [PATCH 457/537] Minor fix --- packages/purpose-process/src/model/domain/toEvent.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index 9bf37a6ca5..1b406eafce 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -2,6 +2,7 @@ import { CreateEvent } from "pagopa-interop-commons"; import { Purpose, PurposeEvent, + PurposeEventV2, PurposeVersionId, toPurposeV2, } from "pagopa-interop-models"; @@ -77,7 +78,7 @@ export const toCreateEventDraftPurposeDeleted = ({ purpose: Purpose; version: number; correlationId: string; -}): CreateEvent => ({ +}): CreateEvent => ({ streamId: purpose.id, version, event: { @@ -96,7 +97,7 @@ export const toCreateEventWaitingForApprovalPurposeDeleted = ({ purpose: Purpose; version: number; correlationId: string; -}): CreateEvent => ({ +}): CreateEvent => ({ streamId: purpose.id, version, event: { From d5f2fee5288ee4fda242984b01d96f06e14956b1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:35:25 +0200 Subject: [PATCH 458/537] Minor fix --- packages/purpose-process/src/model/domain/toEvent.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index a4f4ad498e..677baa7c45 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -2,6 +2,7 @@ import { CreateEvent } from "pagopa-interop-commons"; import { Purpose, PurposeEvent, + PurposeEventV2, PurposeVersionId, toPurposeV2, } from "pagopa-interop-models"; @@ -117,7 +118,7 @@ export const toCreateEventPurposeArchived = ({ purposeVersionId: PurposeVersionId; version: number; correlationId: string; -}): CreateEvent => ({ +}): CreateEvent => ({ streamId: purpose.id, version, event: { From 19bd2ec60b40d2c7f9ab93aa25c966bd5662d348 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:35:52 +0200 Subject: [PATCH 459/537] Minor fix --- packages/purpose-process/src/model/domain/toEvent.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index e7ef1b96de..a464cee50c 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -2,6 +2,7 @@ import { CreateEvent } from "pagopa-interop-commons"; import { Purpose, PurposeEvent, + PurposeEventV2, PurposeVersionId, toPurposeV2, } from "pagopa-interop-models"; @@ -138,7 +139,7 @@ export const toCreateEventPurposeSuspendedByConsumer = ({ purposeVersionId: PurposeVersionId; version: number; correlationId: string; -}): CreateEvent => ({ +}): CreateEvent => ({ streamId: purpose.id, version, event: { @@ -159,7 +160,7 @@ export const toCreateEventPurposeSuspendedByProducer = ({ purposeVersionId: PurposeVersionId; version: number; correlationId: string; -}): CreateEvent => ({ +}): CreateEvent => ({ streamId: purpose.id, version, event: { From eadbc5e2552fd304bcc6f74bec7d6db0231af2bd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:36:18 +0200 Subject: [PATCH 460/537] Minor fix --- packages/purpose-process/src/model/domain/toEvent.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index daf92445d0..0bfed76912 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -2,6 +2,7 @@ import { CreateEvent } from "pagopa-interop-commons"; import { Purpose, PurposeEvent, + PurposeEventV2, PurposeVersionId, toPurposeV2, } from "pagopa-interop-models"; @@ -173,7 +174,7 @@ export const toCreateEventPurposeSuspendedByProducer = ({ export function toCreateEventPurposeAdded( purpose: Purpose, correlationId: string -): CreateEvent { +): CreateEvent { return { streamId: purpose.id, version: 0, From 409d4418c1aab57efa1efc663940720633d9e7af Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:36:31 +0200 Subject: [PATCH 461/537] Minor fix --- packages/purpose-process/src/model/domain/toEvent.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index 674c94ae9b..2400baf85a 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -2,6 +2,7 @@ import { CreateEvent } from "pagopa-interop-commons"; import { Purpose, PurposeEvent, + PurposeEventV2, PurposeId, PurposeVersionId, toPurposeV2, @@ -199,7 +200,7 @@ export const toCreateEventPurposeCloned = ({ sourcePurposeId: PurposeId; sourceVersionId: PurposeId; correlationId: string; -}): CreateEvent => ({ +}): CreateEvent => ({ streamId: purpose.id, version: 0, event: { From 58559ddddd715094cfff1b34978e1c1667668d79 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:44:10 +0200 Subject: [PATCH 462/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 7 ++++++- packages/purpose-process/src/services/purposeService.ts | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 619ad3a68e..aceb76ef79 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -174,6 +174,7 @@ const purposeRouter = ( "/purposes/:id", authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { await purposeService.deletePurpose({ purposeId: unsafeBrandId(req.params.id), @@ -182,7 +183,11 @@ const purposeRouter = ( }); return res.status(204).end(); } catch (error) { - const errorRes = makeApiProblem(error, deletePurposeErrorMapper); + const errorRes = makeApiProblem( + error, + deletePurposeErrorMapper, + ctx.logger + ); return res.status(errorRes.status).json(errorRes).end(); } } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b9fd74a416..bd13e9af54 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -350,10 +350,12 @@ export function purposeServiceBuilder( purposeId, organizationId, correlationId, + logger, }: { purposeId: PurposeId; organizationId: TenantId; correlationId: string; + logger: Logger; }): Promise { logger.info(`Deleting Purpose ${purposeId}`); From f553055ed14e448ca5fa641133e718d33e93a0f4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 10:44:51 +0200 Subject: [PATCH 463/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index aceb76ef79..79f98625ce 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -180,6 +180,7 @@ const purposeRouter = ( purposeId: unsafeBrandId(req.params.id), organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, + logger: ctx.logger, }); return res.status(204).end(); } catch (error) { From 5fffac59036591ab23119130d24699af6ced2e23 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:01:22 +0200 Subject: [PATCH 464/537] Fix test --- packages/purpose-process/test/testDeletePurpose.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/purpose-process/test/testDeletePurpose.ts b/packages/purpose-process/test/testDeletePurpose.ts index 61643cd4a9..c34a697e0c 100644 --- a/packages/purpose-process/test/testDeletePurpose.ts +++ b/packages/purpose-process/test/testDeletePurpose.ts @@ -18,6 +18,7 @@ import { decodeProtobufPayload, getMockPurposeVersion, } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, organizationIsNotTheConsumer, @@ -48,6 +49,7 @@ export const testDeletePurpose = (): ReturnType => purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -88,6 +90,7 @@ export const testDeletePurpose = (): ReturnType => purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -128,6 +131,7 @@ export const testDeletePurpose = (): ReturnType => purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -160,6 +164,7 @@ export const testDeletePurpose = (): ReturnType => purposeId: randomId, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(randomId)); }); @@ -182,6 +187,7 @@ export const testDeletePurpose = (): ReturnType => purposeId: mockPurpose.id, organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( organizationIsNotTheConsumer(mockEService.producerId) @@ -213,6 +219,7 @@ export const testDeletePurpose = (): ReturnType => purposeId: mockPurpose.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeCannotBeDeleted(mockPurpose.id)); } From d07ebda43dcb401adb2ba0b21d0be924cc8f04d5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:07:11 +0200 Subject: [PATCH 465/537] Fix --- packages/purpose-process/test/testRejectPurposeVersion.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/purpose-process/test/testRejectPurposeVersion.ts b/packages/purpose-process/test/testRejectPurposeVersion.ts index ba09c1ccc1..8dcc9abf3d 100644 --- a/packages/purpose-process/test/testRejectPurposeVersion.ts +++ b/packages/purpose-process/test/testRejectPurposeVersion.ts @@ -18,6 +18,7 @@ import { PurposeVersionId, } from "pagopa-interop-models"; import { describe, expect, it, vi } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, eserviceNotFound, @@ -60,6 +61,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "test", organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -116,6 +118,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "test", organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(randomId)); }); @@ -137,6 +140,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "test", organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(eserviceNotFound(mockEService.id)); }); @@ -159,6 +163,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "test", organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( organizationIsNotTheProducer(mockPurpose.consumerId) @@ -184,6 +189,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "test", organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) @@ -215,6 +221,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "test", organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) @@ -243,6 +250,7 @@ export const testRejectPurposeVersion = (): ReturnType => rejectionReason: "", organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(missingRejectionReason()); }); From fcac6208e5cf871edfeafe96a7c9d0c0c210204f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:09:34 +0200 Subject: [PATCH 466/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 5 ++++- packages/purpose-process/src/services/purposeService.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 253de26533..f130b9c0ce 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -295,12 +295,14 @@ const purposeRouter = ( "/purposes/:purposeId/versions/:versionId/archive", authorizationMiddleware([ADMIN_ROLE, INTERNAL_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { const archivedVersion = await purposeService.archivePurposeVersion({ purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, + logger: ctx.logger, }); return res .status(200) @@ -309,7 +311,8 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - archivePurposeVersionErrorMapper + archivePurposeVersionErrorMapper, + ctx.logger ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 4046a58a34..0609df4b86 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -388,11 +388,13 @@ export function purposeServiceBuilder( versionId, organizationId, correlationId, + logger, }: { purposeId: PurposeId; versionId: PurposeVersionId; organizationId: TenantId; correlationId: string; + logger: Logger; }): Promise { logger.info(`Archiving Version ${versionId} in Purpose ${purposeId}`); From 23e5c2fec29085ffc9850c95c43ef911506ff50b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:13:43 +0200 Subject: [PATCH 467/537] Fix --- packages/purpose-process/test/testArchivePurposeVersion.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/purpose-process/test/testArchivePurposeVersion.ts b/packages/purpose-process/test/testArchivePurposeVersion.ts index 706230cdfe..55e1152d11 100644 --- a/packages/purpose-process/test/testArchivePurposeVersion.ts +++ b/packages/purpose-process/test/testArchivePurposeVersion.ts @@ -17,6 +17,7 @@ import { TenantId, } from "pagopa-interop-models"; import { describe, expect, it, vi } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, organizationIsNotTheConsumer, @@ -51,6 +52,7 @@ export const testArchivePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -110,6 +112,7 @@ export const testArchivePurposeVersion = (): ReturnType => versionId: mockPurposeVersion1.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -158,6 +161,7 @@ export const testArchivePurposeVersion = (): ReturnType => versionId: randomVersionId, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(randomPurposeId)); }); @@ -181,6 +185,7 @@ export const testArchivePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: randomOrganizationId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( organizationIsNotTheConsumer(randomOrganizationId) @@ -201,6 +206,7 @@ export const testArchivePurposeVersion = (): ReturnType => versionId: randomVersionId, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) @@ -230,6 +236,7 @@ export const testArchivePurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) From 87cf4062cf692e29531640fb3af60ec0b8fc4f6b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:21:56 +0200 Subject: [PATCH 468/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 5 ++++- packages/purpose-process/src/services/purposeService.ts | 2 ++ .../purpose-process/test/testSuspendPurposeVersion.ts | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index dc761efce6..ed1dac2548 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -291,12 +291,14 @@ const purposeRouter = ( "/purposes/:purposeId/versions/:versionId/suspend", authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { const suspendedVersion = await purposeService.suspendPurposeVersion({ purposeId: unsafeBrandId(req.params.purposeId), versionId: unsafeBrandId(req.params.versionId), organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, + logger: ctx.logger, }); return res .status(200) @@ -305,7 +307,8 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - suspendPurposeVersionErrorMapper + suspendPurposeVersionErrorMapper, + ctx.logger ); return res.status(errorRes.status).json(errorRes).end(); } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 82f8bb4633..ee04e3b86c 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -443,11 +443,13 @@ export function purposeServiceBuilder( versionId, organizationId, correlationId, + logger, }: { purposeId: PurposeId; versionId: PurposeVersionId; organizationId: TenantId; correlationId: string; + logger: Logger; }): Promise { logger.info(`Suspending Version ${versionId} in Purpose ${purposeId}`); diff --git a/packages/purpose-process/test/testSuspendPurposeVersion.ts b/packages/purpose-process/test/testSuspendPurposeVersion.ts index 8329822556..410e068f6f 100644 --- a/packages/purpose-process/test/testSuspendPurposeVersion.ts +++ b/packages/purpose-process/test/testSuspendPurposeVersion.ts @@ -20,6 +20,7 @@ import { PurposeVersionId, TenantId, } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { purposeNotFound, purposeVersionNotFound, @@ -58,6 +59,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: mockPurposeVersion1.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -118,6 +120,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: mockPurposeVersion1.id, organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -179,6 +182,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: mockPurposeVersion1.id, organizationId: mockEService.producerId, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -229,6 +233,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: randomVersionId, organizationId: generateId(), correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(randomPurposeId)); }); @@ -251,6 +256,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: randomVersionId, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( purposeVersionNotFound(mockPurpose.id, randomVersionId) @@ -278,6 +284,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: randomId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(organizationNotAllowed(randomId)); }); @@ -308,6 +315,7 @@ export const testSuspendPurposeVersion = (): ReturnType => versionId: mockPurposeVersion.id, organizationId: mockPurpose.consumerId, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( notValidVersionState(mockPurposeVersion.id, mockPurposeVersion.state) From 810634e5f485282c61124096e3c6f954c8555110 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:24:15 +0200 Subject: [PATCH 469/537] Minor refactor --- .../purpose-process/src/services/purposeService.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index ee04e3b86c..8308715516 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -454,6 +454,11 @@ export function purposeServiceBuilder( logger.info(`Suspending Version ${versionId} in Purpose ${purposeId}`); const purpose = await retrievePurpose(purposeId, readModelService); + const purposeVersion = retrievePurposeVersion(versionId, purpose); + + if (!isSuspendable(purposeVersion)) { + throw notValidVersionState(purposeVersion.id, purposeVersion.state); + } const eservice = await retrieveEService( purpose.data.eserviceId, @@ -466,12 +471,6 @@ export function purposeServiceBuilder( consumerId: purpose.data.consumerId, }); - const purposeVersion = retrievePurposeVersion(versionId, purpose); - - if (!isSuspendable(purposeVersion)) { - throw notValidVersionState(purposeVersion.id, purposeVersion.state); - } - const suspendedPurposeVersion: PurposeVersion = { ...purposeVersion, state: purposeVersionState.suspended, From 4b8923fd7e793ee3abbbe4e9a768661294f5af94 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:34:04 +0200 Subject: [PATCH 470/537] Fix --- packages/purpose-process/src/services/readModelService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index fa7542ed2a..dffae31a5c 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -240,7 +240,7 @@ export function readModelServiceBuilder( totalCount: await ReadModelRepository.getTotalCount( purposes, aggregationPipeline, - true + false ), }; }, From 065385dfafb434162a851ae939012f135f2a2e8c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:35:28 +0200 Subject: [PATCH 471/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 6 ++++-- packages/purpose-process/src/services/purposeService.ts | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index b20044b6aa..76bfbf8bcb 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -77,6 +77,7 @@ const purposeRouter = ( SUPPORT_ROLE, ]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { const { name, @@ -98,7 +99,8 @@ const purposeRouter = ( states: states.map(apiPurposeVersionStateToPurposeVersionState), excludeDraft, }, - { offset, limit } + { offset, limit }, + ctx.logger ); return res .status(200) @@ -110,7 +112,7 @@ const purposeRouter = ( }) .end(); } catch (error) { - const errorRes = makeApiProblem(error, () => 500); + const errorRes = makeApiProblem(error, () => 500, ctx.logger); return res.status(errorRes.status).json(errorRes).end(); } } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 6208c6358f..130159197a 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -513,7 +513,8 @@ export function purposeServiceBuilder( async getPurposes( organizationId: TenantId, filters: ApiGetPurposesFilters, - { offset, limit }: { offset: number; limit: number } + { offset, limit }: { offset: number; limit: number }, + logger: Logger ): Promise> { logger.info( `Getting Purposes with name = ${filters.name}, eservicesIds = ${filters.eservicesIds}, consumers = ${filters.consumersIds}, producers = ${filters.producersIds}, states = ${filters.states}, excludeDraft = ${filters.excludeDraft}, limit = ${limit}, offset = ${offset}` From 070ede81e3c698a43621ea0e7c3bc33d95972df4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 11:37:19 +0200 Subject: [PATCH 472/537] Fix test --- .../purpose-process/test/testGetPurposes.ts | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/purpose-process/test/testGetPurposes.ts b/packages/purpose-process/test/testGetPurposes.ts index 2d5d4b2c36..3d28fd400f 100644 --- a/packages/purpose-process/test/testGetPurposes.ts +++ b/packages/purpose-process/test/testGetPurposes.ts @@ -15,6 +15,7 @@ import { writeInReadmodel, getMockValidRiskAnalysisForm, } from "pagopa-interop-commons-test/index.js"; +import { genericLogger } from "pagopa-interop-commons"; import { addOnePurpose, getMockEService } from "./utils.js"; import { postgresDB, @@ -161,7 +162,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(3); expect(result.results).toEqual([ @@ -180,7 +182,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(2); expect(result.results).toEqual([mockPurpose1, mockPurpose2]); @@ -195,7 +198,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(3); expect(result.results).toEqual([ @@ -214,7 +218,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(2); expect(result.results).toEqual([mockPurpose4, mockPurpose6]); @@ -229,7 +234,8 @@ export const testGetPurposes = (): ReturnType => states: [purposeVersionState.rejected, purposeVersionState.archived], excludeDraft: undefined, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(3); expect(result.results).toEqual([ @@ -248,7 +254,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: true, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(4); expect(result.results).toEqual([ @@ -268,7 +275,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: false, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(7); expect(result.results).toEqual([ @@ -291,7 +299,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 5, limit: 50 } + { offset: 5, limit: 50 }, + genericLogger ); expect(result.results).toEqual([mockPurpose6, mockPurpose7]); }); @@ -305,7 +314,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 0, limit: 3 } + { offset: 0, limit: 3 }, + genericLogger ); expect(result.results).toEqual([ mockPurpose1, @@ -323,7 +333,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: undefined, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(0); expect(result.results).toEqual([]); @@ -339,7 +350,8 @@ export const testGetPurposes = (): ReturnType => states: [purposeVersionState.archived], excludeDraft: true, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(1); expect(result.results).toEqual([ @@ -357,7 +369,8 @@ export const testGetPurposes = (): ReturnType => states: [purposeVersionState.draft], excludeDraft: false, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(1); expect(result.results).toEqual([mockPurpose1]); @@ -380,7 +393,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: false, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(8); expect(result.results).toEqual( @@ -424,7 +438,8 @@ export const testGetPurposes = (): ReturnType => states: [], excludeDraft: false, }, - { offset: 0, limit: 50 } + { offset: 0, limit: 50 }, + genericLogger ); expect(result.totalCount).toBe(2); expect(result.results).toEqual([ From 0148114b41c533f08921dd84f4c0f5966eb569cf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 12:34:30 +0200 Subject: [PATCH 473/537] Minor refactor --- .../purpose-process/src/model/domain/models.ts | 15 +-------------- .../purpose-process/src/routers/PurposeRouter.ts | 8 ++++---- .../src/services/purposeService.ts | 12 +++++------- .../src/services/readModelService.ts | 16 ++++++++++++---- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index ea3bae9295..22a76d8519 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -1,9 +1,5 @@ import { z } from "zod"; -import { - EServiceId, - PurposeVersionState, - TenantId, -} from "pagopa-interop-models"; +import { ZodiosQueryParamsByPath } from "@zodios/core"; import * as api from "../generated/api.js"; export type ApiRiskAnalysisForm = z.infer; @@ -27,12 +23,3 @@ export type ApiRiskAnalysisFormSeed = z.infer< export type ApiReversePurposeUpdateContent = z.infer< typeof api.schemas.ReversePurposeUpdateContent >; - -export type ApiGetPurposesFilters = { - name?: string; - eservicesIds: EServiceId[]; - consumersIds: TenantId[]; - producersIds: TenantId[]; - states: PurposeVersionState[]; - excludeDraft: boolean | undefined; -}; diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 76bfbf8bcb..5c4e05a94a 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -93,10 +93,10 @@ const purposeRouter = ( req.ctx.authData.organizationId, { name, - eservicesIds: eservicesIds.map(unsafeBrandId), - consumersIds: consumersIds.map(unsafeBrandId), - producersIds: producersIds.map(unsafeBrandId), - states: states.map(apiPurposeVersionStateToPurposeVersionState), + eservicesIds: eservicesIds?.map(unsafeBrandId), + consumersIds: consumersIds?.map(unsafeBrandId), + producersIds: producersIds?.map(unsafeBrandId), + states: states?.map(apiPurposeVersionStateToPurposeVersionState), excludeDraft, }, { offset, limit }, diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 130159197a..1c8829b8fb 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -55,9 +55,8 @@ import { import { ApiPurposeUpdateContent, ApiReversePurposeUpdateContent, - ApiGetPurposesFilters, } from "../model/domain/models.js"; -import { ReadModelService } from "./readModelService.js"; +import { GetPurposesFilters, ReadModelService } from "./readModelService.js"; import { assertOrganizationIsAConsumer, assertEserviceMode, @@ -512,7 +511,7 @@ export function purposeServiceBuilder( }, async getPurposes( organizationId: TenantId, - filters: ApiGetPurposesFilters, + filters: GetPurposesFilters, { offset, limit }: { offset: number; limit: number }, logger: Logger ): Promise> { @@ -520,11 +519,10 @@ export function purposeServiceBuilder( `Getting Purposes with name = ${filters.name}, eservicesIds = ${filters.eservicesIds}, consumers = ${filters.consumersIds}, producers = ${filters.producersIds}, states = ${filters.states}, excludeDraft = ${filters.excludeDraft}, limit = ${limit}, offset = ${offset}` ); - const purposesList = await readModelService.getPurposes( - filters, + const purposesList = await readModelService.getPurposes(filters, { offset, - limit - ); + limit, + }); const mappingPurposeEservice = await Promise.all( purposesList.results.map(async (purpose) => { diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index dffae31a5c..0658d7e572 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -18,10 +18,19 @@ import { PurposeReadModel, ListResult, purposeVersionState, + PurposeVersionState, } from "pagopa-interop-models"; import { Filter, WithId } from "mongodb"; import { z } from "zod"; -import { ApiGetPurposesFilters } from "../model/domain/models.js"; + +export type GetPurposesFilters = { + name?: string; + eservicesIds: EServiceId[]; + consumersIds: TenantId[]; + producersIds: TenantId[]; + states: PurposeVersionState[]; + excludeDraft: boolean | undefined; +}; async function getPurpose( purposes: PurposeCollection, @@ -127,9 +136,8 @@ export function readModelServiceBuilder( } satisfies ReadModelFilter); }, async getPurposes( - filters: ApiGetPurposesFilters, - offset: number, - limit: number + filters: GetPurposesFilters, + { offset, limit }: { offset: number; limit: number } ): Promise> { const { name, From 16244bcc14106337185a72cd04d15b6fd34de0d7 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 12:37:32 +0200 Subject: [PATCH 474/537] Fix import --- packages/purpose-process/src/model/domain/models.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index 22a76d8519..0b29388e96 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -1,5 +1,4 @@ import { z } from "zod"; -import { ZodiosQueryParamsByPath } from "@zodios/core"; import * as api from "../generated/api.js"; export type ApiRiskAnalysisForm = z.infer; From fc82d163880ce21c5411b43233f2ca5a7352a56d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 14:41:59 +0200 Subject: [PATCH 475/537] Update model --- packages/models/src/purpose/purpose.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index feecc4e13c..9653a0bebe 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -33,7 +33,6 @@ export type PurposeVersionDocument = z.infer; export const PurposeVersion = z.object({ id: PurposeVersionId, state: PurposeVersionState, - expectedApprovalDate: z.coerce.date().optional(), riskAnalysis: PurposeVersionDocument.optional(), dailyCalls: z.number(), rejectionReason: z.string().optional(), From 6d3b0dc6e858da1a585c48acfceb9feee9838e1f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 14:58:45 +0200 Subject: [PATCH 476/537] Remove field --- packages/models/src/purpose/protobufConverterFromV1.ts | 1 - packages/models/src/purpose/protobufConverterFromV2.ts | 3 --- packages/models/src/purpose/protobufConverterToV1.ts | 2 +- packages/models/src/purpose/protobufConverterToV2.ts | 1 - packages/purpose-process/open-api/purpose-service-spec.yml | 3 --- packages/purpose-process/src/model/domain/apiConverter.ts | 1 - 6 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/models/src/purpose/protobufConverterFromV1.ts b/packages/models/src/purpose/protobufConverterFromV1.ts index 5e77fdbca5..97c1f3a4a7 100644 --- a/packages/models/src/purpose/protobufConverterFromV1.ts +++ b/packages/models/src/purpose/protobufConverterFromV1.ts @@ -52,7 +52,6 @@ export const fromPurposeVersionV1 = ( ...input, id: unsafeBrandId(input.id), state: fromPurposeVersionStateV1(input.state), - expectedApprovalDate: bigIntToDate(input.expectedApprovalDate), riskAnalysis: input.riskAnalysis ? fromPurposeVersionDocumentV1(input.riskAnalysis) : undefined, diff --git a/packages/models/src/purpose/protobufConverterFromV2.ts b/packages/models/src/purpose/protobufConverterFromV2.ts index cf0ad44817..17a5eae789 100644 --- a/packages/models/src/purpose/protobufConverterFromV2.ts +++ b/packages/models/src/purpose/protobufConverterFromV2.ts @@ -49,9 +49,6 @@ export const fromPurposeVersionV2 = ( ...input, id: unsafeBrandId(input.id), state: fromPurposeVersionStateV2(input.state), - expectedApprovalDate: input.expectedApprovalDate - ? new Date(Number(input.expectedApprovalDate)) - : undefined, riskAnalysis: input.riskAnalysis ? fromPurposeVersionDocumentV2(input.riskAnalysis) : undefined, diff --git a/packages/models/src/purpose/protobufConverterToV1.ts b/packages/models/src/purpose/protobufConverterToV1.ts index 49b819e5ca..4a410d0453 100644 --- a/packages/models/src/purpose/protobufConverterToV1.ts +++ b/packages/models/src/purpose/protobufConverterToV1.ts @@ -42,7 +42,7 @@ export const toPurposeVersionV1 = ( ): PurposeVersionV1 => ({ ...input, state: toPurposeVersionStateV1(input.state), - expectedApprovalDate: dateToBigInt(input.expectedApprovalDate), + expectedApprovalDate: undefined, createdAt: dateToBigInt(input.createdAt), updatedAt: dateToBigInt(input.updatedAt), firstActivationAt: dateToBigInt(input.firstActivationAt), diff --git a/packages/models/src/purpose/protobufConverterToV2.ts b/packages/models/src/purpose/protobufConverterToV2.ts index 735669d8f9..7f8bc4d042 100644 --- a/packages/models/src/purpose/protobufConverterToV2.ts +++ b/packages/models/src/purpose/protobufConverterToV2.ts @@ -41,7 +41,6 @@ export const toPurposeVersionV2 = ( ): PurposeVersionV2 => ({ ...input, state: toPurposeVersionStateV2(input.state), - expectedApprovalDate: dateToBigInt(input.expectedApprovalDate), createdAt: dateToBigInt(input.createdAt), updatedAt: dateToBigInt(input.updatedAt), firstActivationAt: dateToBigInt(input.firstActivationAt), diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index 8fd75f6ae9..ae0abc1cdb 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -1152,9 +1152,6 @@ components: firstActivationAt: type: string format: date-time - expectedApprovalDate: - type: string - format: date-time dailyCalls: description: "maximum number of daily calls that this version can perform." type: integer diff --git a/packages/purpose-process/src/model/domain/apiConverter.ts b/packages/purpose-process/src/model/domain/apiConverter.ts index a662f9c649..ae73152bb4 100644 --- a/packages/purpose-process/src/model/domain/apiConverter.ts +++ b/packages/purpose-process/src/model/domain/apiConverter.ts @@ -115,7 +115,6 @@ export const purposeVersionToApiPurposeVersion = ( createdAt: version.createdAt.toJSON(), updatedAt: version.updatedAt?.toJSON(), firstActivationAt: version.firstActivationAt?.toJSON(), - expectedApprovalDate: version.expectedApprovalDate?.toJSON(), riskAnalysis: version.riskAnalysis ? purposeVersionDocumentToApiPurposeVersionDocument(version.riskAnalysis) : undefined, From 23078eb678631306dc2915994293e86fcd24e2bd Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 14:58:54 +0200 Subject: [PATCH 477/537] Remove endpoint --- .../open-api/purpose-service-spec.yml | 61 ------------------- .../src/routers/PurposeRouter.ts | 5 -- 2 files changed, 66 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index ae0abc1cdb..a1b704a1ec 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -660,57 +660,6 @@ paths: tags: - purpose description: archives the purpose version by id - /purposes/{purposeId}/versions/{versionId}/update/waitingForApproval: - parameters: - - $ref: "#/components/parameters/CorrelationIdHeader" - - name: purposeId - in: path - required: true - schema: - type: string - format: uuid - - name: versionId - in: path - required: true - schema: - type: string - format: uuid - post: - tags: - - purpose - summary: Update a purpose version in waiting for approval - operationId: updateWaitingForApprovalPurposeVersion - requestBody: - content: - application/json: - schema: - $ref: "#/components/schemas/WaitingForApprovalPurposeVersionUpdateContent" - required: true - responses: - "200": - description: Purpose Version updated - content: - application/json: - schema: - $ref: "#/components/schemas/PurposeVersion" - "400": - description: Invalid input - content: - application/json: - schema: - $ref: "#/components/schemas/Problem" - "403": - description: Purpose version not in waiting for approval state or the organization is not a producer - content: - application/json: - schema: - $ref: "#/components/schemas/Problem" - "404": - description: Not Found - content: - application/json: - schema: - $ref: "#/components/schemas/Problem" /purposes/riskAnalysis/latest: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" @@ -1122,16 +1071,6 @@ components: - description - isFreeOfCharge - dailyCalls - WaitingForApprovalPurposeVersionUpdateContent: - type: object - description: contains the expected payload for purpose version update. - properties: - expectedApprovalDate: - description: "Estimated expected approval date for a purpose version" - type: string - format: date-time - required: - - expectedApprovalDate PurposeVersion: type: object properties: diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index c6b88575b4..bcca444ee7 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -398,11 +398,6 @@ const purposeRouter = ( } } ) - .post( - "/purposes/:purposeId/versions/:versionId/update/waitingForApproval", - authorizationMiddleware([ADMIN_ROLE]), - (_req, res) => res.status(501).send() - ) .get( "/purposes/riskAnalysis/latest", authorizationMiddleware([ADMIN_ROLE, SUPPORT_ROLE]), From 55456b8e0d38c4d904d4028f44f418173050628f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:06:51 +0200 Subject: [PATCH 478/537] Revert "fix test" This reverts commit 58fd048eb389ac7a53e4c54b0a027fa24b69bc60. --- .../test/testGetLatestRiskAnalysisConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts index 09b7631ada..6d76b6c19a 100644 --- a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts +++ b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts @@ -63,7 +63,7 @@ export const testGetLatestRiskAnalysisConfiguration = (): ReturnType< }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); - it("should throw riskAnalysisConfigLatestVersionNotFound if a config with that kind doesn't exist", async () => { + it("should throw riskAnalysisConfigLatestVersionNotFound if a config with that version doesn't exist", async () => { const mockTenant = { ...getMockTenant(), kind: tenantKind.PA, From b25d06099a6a2318d36569ca0b41720efe5f6568 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:06:58 +0200 Subject: [PATCH 479/537] Revert "missed test's call" This reverts commit 8c6de7b26e42cf5f598ede66aff2ae0f22e93767. --- packages/purpose-process/test/purposeService.integration.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index f5067f9179..d992a67492 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -111,6 +111,5 @@ describe("Integration tests", async () => { testCreateReversePurpose(); testClonePurpose(); testGetRiskAnalysisConfigurationByVersion(); - testGetLatestRiskAnalysisConfiguration(); }); }); From 959041e1537b78bb1864fdeafdfcdf056b72c5f8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:07:00 +0200 Subject: [PATCH 480/537] Revert "missed error in erroMapper" This reverts commit 2b41c0dfb6bbce78ea6867390f522ee503d36bf6. --- packages/purpose-process/src/utilities/errorMappers.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 2faf6ab8d8..b83abbb0cb 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -185,10 +185,5 @@ export const retrieveLatestRiskAnalysisConfigurationErrorMapper = ( error: ApiError ): number => match(error.code) - .with( - "tenantNotFound", - "tenantKindNotFound", - "riskAnalysisConfigLatestVersionNotFound", - () => HTTP_STATUS_BAD_REQUEST - ) + .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From d6c0bad3257e864f701d30f0d71773e97ff97b47 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:07:03 +0200 Subject: [PATCH 481/537] Revert "Merge branch 'IMN-452_retrieveRiskAnalysisConfigurationByVersion' into IMN-451_retrieveLatestRiskAnalysis" This reverts commit 18cdefcb25e6b4e0379a426679ee35f073fb3615, reversing changes made to be95364de808b681ce3681bfa66d35e4fcdfb450. --- .../test/testGetRiskAnalysisConfigurationByVersion.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts index ba9223d25e..1df0d832e9 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts @@ -16,7 +16,7 @@ import { import { describe, expect, it } from "vitest"; import { getFormRulesByVersion } from "pagopa-interop-commons"; import { - riskAnalysisConfigVersionNotFound, + RiskAnalysisConfigVersionNotFound, eserviceNotFound, tenantKindNotFound, tenantNotFound, @@ -133,7 +133,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< organizationId: mockTenant.id, }) ).rejects.toThrowError( - riskAnalysisConfigVersionNotFound( + RiskAnalysisConfigVersionNotFound( wrongRiskAnalysisVersion, mockTenant.kind ) From 4ad0337ca00fcb2bf39a249c45199f548ba6562d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:07:20 +0200 Subject: [PATCH 482/537] Revert "Merge branch 'IMN-452_retrieveRiskAnalysisConfigurationByVersion' into IMN-451_retrieveLatestRiskAnalysis" This reverts commit be95364de808b681ce3681bfa66d35e4fcdfb450, reversing changes made to 18e43a3a376e5d3304c9bd15887a273502e4804c. --- packages/purpose-process/src/model/domain/errors.ts | 2 +- packages/purpose-process/src/services/purposeService.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index a522e26326..ea9eddf016 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -247,7 +247,7 @@ export function purposeCannotBeCloned( }); } -export function riskAnalysisConfigVersionNotFound( +export function RiskAnalysisConfigVersionNotFound( version: string, tenantKind: TenantKind ): ApiError { diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 9871532051..1ffcac4d99 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -56,9 +56,9 @@ import { purposeVersionDocumentNotFound, purposeVersionNotFound, tenantNotFound, - riskAnalysisConfigVersionNotFound, - riskAnalysisConfigLatestVersionNotFound, + RiskAnalysisConfigVersionNotFound, tenantKindNotFound, + riskAnalysisConfigLatestVersionNotFound, } from "../model/domain/errors.js"; import { toCreateEventDraftPurposeDeleted, @@ -871,7 +871,7 @@ export function purposeServiceBuilder( ); if (!riskAnalysisFormConfig) { - throw riskAnalysisConfigVersionNotFound( + throw RiskAnalysisConfigVersionNotFound( riskAnalysisVersion, tenant.kind ); From 6a49c70f0707bf959e1d9cc730777c35ddabd4a6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:07:31 +0200 Subject: [PATCH 483/537] Revert "implemented retrieveLatestRiskAnalysis" This reverts commit 18e43a3a376e5d3304c9bd15887a273502e4804c. --- .../test/getFormRules.unit.test.ts | 18 +--- .../risk-analysis/riskAnalysisValidation.ts | 2 +- .../src/model/domain/errors.ts | 11 --- .../src/routers/PurposeRouter.ts | 25 +----- .../src/services/purposeService.ts | 26 ------ .../src/utilities/errorMappers.ts | 7 -- .../test/purposeService.integration.test.ts | 1 - .../testGetLatestRiskAnalysisConfiguration.ts | 82 ------------------- 8 files changed, 3 insertions(+), 169 deletions(-) delete mode 100644 packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts diff --git a/packages/commons-test/test/getFormRules.unit.test.ts b/packages/commons-test/test/getFormRules.unit.test.ts index 43218dc6ad..3b6aa2360c 100644 --- a/packages/commons-test/test/getFormRules.unit.test.ts +++ b/packages/commons-test/test/getFormRules.unit.test.ts @@ -1,11 +1,4 @@ -import { - getFormRulesByVersion, - getLatestVersionFormRules, - pa1, - pa3, - private1, - private2, -} from "pagopa-interop-commons"; +import { getFormRulesByVersion, pa1, private1 } from "pagopa-interop-commons"; import { tenantKind } from "pagopa-interop-models"; import { describe, it, expect } from "vitest"; @@ -24,13 +17,4 @@ describe("Form rules retrieve", () => { expect(getFormRulesByVersion(tenantKind.PA, "0.0")).toBeUndefined(); }); }); - describe("getLatestVersionFormRules", () => { - it.each(Object.values(tenantKind))( - "should retrieve latest form rules for kind %s", - (kind) => { - const riskAnalysisFormConfig = kind === tenantKind.PA ? pa3 : private2; // TO BE UPDATED with latest versions - expect(getLatestVersionFormRules(kind)).toEqual(riskAnalysisFormConfig); - } - ); - }); }); diff --git a/packages/commons/src/risk-analysis/riskAnalysisValidation.ts b/packages/commons/src/risk-analysis/riskAnalysisValidation.ts index 3c6c7af5e1..7cbc139ef0 100644 --- a/packages/commons/src/risk-analysis/riskAnalysisValidation.ts +++ b/packages/commons/src/risk-analysis/riskAnalysisValidation.ts @@ -99,7 +99,7 @@ function getSanitizedAnswers( ); } -export function getLatestVersionFormRules( +function getLatestVersionFormRules( tenantKind: TenantKind ): RiskAnalysisFormRules | undefined { const rules = riskAnalysisFormRules[tenantKind]; diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index ea9eddf016..b88d566ed9 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -36,7 +36,6 @@ export const errorCodes = { eserviceRiskAnalysisNotFound: "0020", purposeCannotBeCloned: "0021", riskAnalysisConfigVersionNotFound: "0022", - riskAnalysisConfigLatestVersionNotFound: "0023", }; export type ErrorCodes = keyof typeof errorCodes; @@ -257,13 +256,3 @@ export function RiskAnalysisConfigVersionNotFound( title: "Risk Analysis config version not found", }); } - -export function riskAnalysisConfigLatestVersionNotFound( - tenantKind: TenantKind -): ApiError { - return new ApiError({ - detail: `Latest Risk Analysis Configuration for tenant kind ${tenantKind} not found`, - code: "riskAnalysisConfigLatestVersionNotFound", - title: "Risk Analysis config latest version not found", - }); -} diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index bcca444ee7..ce7ce2441c 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -31,7 +31,6 @@ import { getPurposeErrorMapper, getRiskAnalysisDocumentErrorMapper, rejectPurposeVersionErrorMapper, - retrieveLatestRiskAnalysisConfigurationErrorMapper, retrieveRiskAnalysisConfigurationByVersionErrorMapper, suspendPurposeVersionErrorMapper, updatePurposeErrorMapper, @@ -401,29 +400,7 @@ const purposeRouter = ( .get( "/purposes/riskAnalysis/latest", authorizationMiddleware([ADMIN_ROLE, SUPPORT_ROLE]), - async (req, res) => { - try { - const riskAnalysisConfiguration = - await purposeService.retrieveLatestRiskAnalysisConfiguration({ - tenantKind: req.query.tenantKind, - organizationId: req.ctx.authData.organizationId, - }); - return res - .status(200) - .json( - riskAnalysisFormConfigToApiRiskAnalysisFormConfig( - riskAnalysisConfiguration - ) - ) - .end(); - } catch (error) { - const errorRes = makeApiProblem( - error, - retrieveLatestRiskAnalysisConfigurationErrorMapper - ); - return res.status(errorRes.status).json(errorRes).end(); - } - } + (_req, res) => res.status(501).send() ) .get( "/purposes/riskAnalysis/version/:riskAnalysisVersion", diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1ffcac4d99..1077234454 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -5,7 +5,6 @@ import { eventRepository, formatDateAndTime, getFormRulesByVersion, - getLatestVersionFormRules, logger, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, @@ -57,8 +56,6 @@ import { purposeVersionNotFound, tenantNotFound, RiskAnalysisConfigVersionNotFound, - tenantKindNotFound, - riskAnalysisConfigLatestVersionNotFound, } from "../model/domain/errors.js"; import { toCreateEventDraftPurposeDeleted, @@ -877,29 +874,6 @@ export function purposeServiceBuilder( ); } - return riskAnalysisFormConfig; - }, - async retrieveLatestRiskAnalysisConfiguration({ - tenantKind, - organizationId, - }: { - tenantKind: TenantKind | undefined; - organizationId: TenantId; - }): Promise { - logger.info(`Retrieve latest risk analysis configuration`); - - const tenant = await retrieveTenant(organizationId, readModelService); - const kind = tenantKind || tenant.kind; - - if (!kind) { - throw tenantKindNotFound(tenant.id); - } - - const riskAnalysisFormConfig = getLatestVersionFormRules(kind); - if (!riskAnalysisFormConfig) { - throw riskAnalysisConfigLatestVersionNotFound(kind); - } - return riskAnalysisFormConfig; }, }; diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index b83abbb0cb..c8bf32f981 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -180,10 +180,3 @@ export const retrieveRiskAnalysisConfigurationByVersionErrorMapper = ( ) .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); - -export const retrieveLatestRiskAnalysisConfigurationErrorMapper = ( - error: ApiError -): number => - match(error.code) - .with("tenantNotFound", "tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) - .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/purpose-process/test/purposeService.integration.test.ts b/packages/purpose-process/test/purposeService.integration.test.ts index d992a67492..3a4e01af88 100644 --- a/packages/purpose-process/test/purposeService.integration.test.ts +++ b/packages/purpose-process/test/purposeService.integration.test.ts @@ -42,7 +42,6 @@ import { testCreatePurpose } from "./testCreatePurpose.js"; import { testCreateReversePurpose } from "./testCreateReversePurpose.js"; import { testClonePurpose } from "./testClonePurpose.js"; import { testGetRiskAnalysisConfigurationByVersion } from "./testGetRiskAnalysisConfigurationByVersion.js"; -import { testGetLatestRiskAnalysisConfiguration } from "./testGetLatestRiskAnalysisConfiguration.js"; export let purposes: PurposeCollection; export let eservices: EServiceCollection; diff --git a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts b/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts deleted file mode 100644 index 6d76b6c19a..0000000000 --- a/packages/purpose-process/test/testGetLatestRiskAnalysisConfiguration.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ -import { - getMockTenant, - writeInReadmodel, -} from "pagopa-interop-commons-test/index.js"; -import { - TenantId, - TenantKind, - generateId, - tenantKind, -} from "pagopa-interop-models"; -import { describe, expect, it } from "vitest"; -import { getLatestVersionFormRules } from "pagopa-interop-commons"; -import { - tenantKindNotFound, - tenantNotFound, - riskAnalysisConfigLatestVersionNotFound, -} from "../src/model/domain/errors.js"; -import { purposeService, tenants } from "./purposeService.integration.test.js"; -export const testGetLatestRiskAnalysisConfiguration = (): ReturnType< - typeof describe -> => - describe("retrieveLatestRiskAnalysisConfiguration", async () => { - it.each(Object.values(tenantKind))( - "should retrieve latest risk analysis configuration for kind %s", - async (kind) => { - const mockTenant = { - ...getMockTenant(), - kind, - }; - await writeInReadmodel(mockTenant, tenants); - - const result = - await purposeService.retrieveLatestRiskAnalysisConfiguration({ - tenantKind: kind, - organizationId: mockTenant.id, - }); - - expect(result).toEqual(getLatestVersionFormRules(kind)); - } - ); - it("should throw tenantNotFound if the tenant doesn't exist", async () => { - const randomId = generateId(); - - expect( - purposeService.retrieveLatestRiskAnalysisConfiguration({ - tenantKind: tenantKind.PA, - organizationId: randomId, - }) - ).rejects.toThrowError(tenantNotFound(randomId)); - }); - it("should throw tenantKindNotFound if the tenant kind is undefined", async () => { - const mockTenant = { - ...getMockTenant(), - kind: undefined, - }; - await writeInReadmodel(mockTenant, tenants); - - expect( - purposeService.retrieveLatestRiskAnalysisConfiguration({ - tenantKind: undefined, - organizationId: mockTenant.id, - }) - ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); - }); - it("should throw riskAnalysisConfigLatestVersionNotFound if a config with that version doesn't exist", async () => { - const mockTenant = { - ...getMockTenant(), - kind: tenantKind.PA, - }; - await writeInReadmodel(mockTenant, tenants); - - const kind = "unkown" as TenantKind; - - expect( - purposeService.retrieveLatestRiskAnalysisConfiguration({ - tenantKind: kind, - organizationId: mockTenant.id, - }) - ).rejects.toThrowError(riskAnalysisConfigLatestVersionNotFound(kind)); - }); - }); From 7a086e3f7604f178c3c69759ac96d184ca20d1d3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:13:54 +0200 Subject: [PATCH 484/537] Fix --- .../src/models/purpose/purposeEventNotificationMappers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts index e93dd0a944..cc530badb9 100644 --- a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts @@ -36,7 +36,7 @@ export const toPurposeVersionV1Notification = ( ): PurposeVersionV1Notification => ({ ...input, state: toPurposeVersionStateV1Notification(input.state), - expectedApprovalDate: input.expectedApprovalDate?.toISOString(), + expectedApprovalDate: undefined, createdAt: input.createdAt.toISOString(), updatedAt: input.updatedAt?.toISOString(), firstActivationAt: input.firstActivationAt?.toISOString(), From 366998d231959c4710d5c2b27c1102a420422e00 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:32:07 +0200 Subject: [PATCH 485/537] Improvement --- .../purpose-process/test/testDeletePurposeVersion.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index b91e8ed35f..293097d39e 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { getMockPurposeVersion, getMockPurpose, @@ -33,10 +33,14 @@ import { export const testDeletePurposeVersion = (): ReturnType => describe("deletePurposeVersion", () => { - it("should write in event-store for the deletion of a purpose version", async () => { + beforeAll(() => { vi.useFakeTimers(); vi.setSystemTime(new Date()); - + }); + afterAll(() => { + vi.useRealTimers(); + }); + it("should write in event-store for the deletion of a purpose version", async () => { const mockEService = getMockEService(); const mockPurposeVersion1 = getMockPurposeVersion( purposeVersionState.waitingForApproval From f30c6cf119e5a51411b1264711c8987a77a91a10 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:32:31 +0200 Subject: [PATCH 486/537] Revert "Improvement" This reverts commit 366998d231959c4710d5c2b27c1102a420422e00. --- .../purpose-process/test/testDeletePurposeVersion.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/purpose-process/test/testDeletePurposeVersion.ts b/packages/purpose-process/test/testDeletePurposeVersion.ts index 293097d39e..b91e8ed35f 100644 --- a/packages/purpose-process/test/testDeletePurposeVersion.ts +++ b/packages/purpose-process/test/testDeletePurposeVersion.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { getMockPurposeVersion, getMockPurpose, @@ -33,14 +33,10 @@ import { export const testDeletePurposeVersion = (): ReturnType => describe("deletePurposeVersion", () => { - beforeAll(() => { + it("should write in event-store for the deletion of a purpose version", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); - }); - afterAll(() => { - vi.useRealTimers(); - }); - it("should write in event-store for the deletion of a purpose version", async () => { + const mockEService = getMockEService(); const mockPurposeVersion1 = getMockPurposeVersion( purposeVersionState.waitingForApproval From 492a5e4b813d22175fa1a0f04498e0ad21429023 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 15:33:21 +0200 Subject: [PATCH 487/537] Improvement --- packages/purpose-process/test/testClonePurpose.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts index b07c399d6f..6f25d5f84d 100644 --- a/packages/purpose-process/test/testClonePurpose.ts +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -20,7 +20,7 @@ import { toPurposeV2, unsafeBrandId, } from "pagopa-interop-models"; -import { describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { formatDateAndTime } from "pagopa-interop-commons"; import { duplicatedPurposeTitle, @@ -39,6 +39,13 @@ import { export const testClonePurpose = (): ReturnType => describe("clonePurpose", async () => { + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + afterAll(() => { + vi.useRealTimers(); + }); it("should write on event-store for the cloning of a purpose", async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); From d3b142d24c6ad7a3723c59c093168be01a62abf1 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 9 May 2024 16:08:48 +0200 Subject: [PATCH 488/537] deleted unused error --- .../src/model/domain/errors.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index cee6179e75..ee2085d556 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -23,12 +23,11 @@ export const errorCodes = { purposeVersionCannotBeDeleted: "0009", organizationIsNotTheProducer: "0010", notValidVersionState: "0011", - missingRejectionReason: "0012", - eServiceModeNotAllowed: "0013", - missingFreeOfChargeReason: "0014", - riskAnalysisValidationFailed: "0015", - purposeNotInDraftState: "0016", - duplicatedPurposeTitle: "0017", + eServiceModeNotAllowed: "0012", + missingFreeOfChargeReason: "0013", + riskAnalysisValidationFailed: "0014", + purposeNotInDraftState: "0015", + duplicatedPurposeTitle: "0016", }; export type ErrorCodes = keyof typeof errorCodes; @@ -180,14 +179,6 @@ export function notValidVersionState( }); } -export function missingRejectionReason(): ApiError { - return new ApiError({ - detail: `Rejection reason is missing`, - code: "missingRejectionReason", - title: "Rejection reason can't be omitted", - }); -} - export function duplicatedPurposeTitle(title: string): ApiError { return new ApiError({ detail: `Purpose with title: ${title} already exists`, From 802e75a2987356f0263a1550daa575c9afad92ae Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Thu, 9 May 2024 16:47:17 +0200 Subject: [PATCH 489/537] fix tests --- .../src/routers/PurposeRouter.ts | 10 +++- .../purpose-process/test/testCreatePurpose.ts | 57 ++++++++++++++----- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 4523a3041b..615214e65f 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -122,19 +122,25 @@ const purposeRouter = ( "/purposes", authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = await purposeService.createPurpose( req.body, req.ctx.authData.organizationId, - req.ctx.correlationId + req.ctx.correlationId, + ctx.logger ); return res .status(200) .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) .end(); } catch (error) { - const errorRes = makeApiProblem(error, createPurposeErrorMapper); + const errorRes = makeApiProblem( + error, + createPurposeErrorMapper, + ctx.logger + ); return res.status(errorRes.status).json(errorRes).end(); } } diff --git a/packages/purpose-process/test/testCreatePurpose.ts b/packages/purpose-process/test/testCreatePurpose.ts index 9ad067670b..46c7fef445 100644 --- a/packages/purpose-process/test/testCreatePurpose.ts +++ b/packages/purpose-process/test/testCreatePurpose.ts @@ -16,6 +16,7 @@ import { tenantKind, toPurposeV2, toReadModelEService, + toReadModelAgreement, unsafeBrandId, } from "pagopa-interop-models"; import { describe, expect, it, vi } from "vitest"; @@ -29,7 +30,10 @@ import { getMockPurpose, getMockDescriptor, } from "pagopa-interop-commons-test"; -import { unexpectedRulesVersionError } from "pagopa-interop-commons"; +import { + genericLogger, + unexpectedRulesVersionError, +} from "pagopa-interop-commons"; import { missingFreeOfChargeReason, tenantKindNotFound, @@ -97,13 +101,17 @@ export const testCreatePurpose = (): ReturnType => vi.useFakeTimers(); vi.setSystemTime(new Date()); await writeInReadmodel(tenant, tenants); - await writeInReadmodel(agreementEservice1, agreements); + await writeInReadmodel( + toReadModelAgreement(agreementEservice1), + agreements + ); await writeInReadmodel(toReadModelEService(eService1), eservices); const { purpose } = await purposeService.createPurpose( purposeSeed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ); const writtenEvent = await readLastEventByStreamId( @@ -178,7 +186,8 @@ export const testCreatePurpose = (): ReturnType => purposeService.createPurpose( seed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(missingFreeOfChargeReason()); }); @@ -205,14 +214,18 @@ export const testCreatePurpose = (): ReturnType => }; await writeInReadmodel(tenantWithoutKind, tenants); - await writeInReadmodel(agreementEservice, agreements); + await writeInReadmodel( + toReadModelAgreement(agreementEservice), + agreements + ); await writeInReadmodel(toReadModelEService(eService), eservices); expect( purposeService.createPurpose( seed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(tenantKindNotFound(tenantWithoutKind.id)); }); @@ -221,7 +234,8 @@ export const testCreatePurpose = (): ReturnType => purposeService.createPurpose( purposeSeed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(tenantNotFound(tenant.id)); }); @@ -255,20 +269,24 @@ export const testCreatePurpose = (): ReturnType => }; await writeInReadmodel(tenant, tenants); - await writeInReadmodel(agreement, agreements); + await writeInReadmodel(toReadModelAgreement(agreement), agreements); await writeInReadmodel(toReadModelEService(eService), eservices); expect( purposeService.createPurpose( seed, unsafeBrandId(seed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(agreementNotFound(eService.id, tenant.id)); }); it("should throw organizationIsNotTheConsumer if the requester is not the consumer", async () => { await writeInReadmodel(tenant, tenants); - await writeInReadmodel(agreementEservice1, agreements); + await writeInReadmodel( + toReadModelAgreement(agreementEservice1), + agreements + ); await writeInReadmodel(toReadModelEService(getMockEService()), eservices); const seed: ApiPurposeSeed = { @@ -280,7 +298,8 @@ export const testCreatePurpose = (): ReturnType => purposeService.createPurpose( seed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError( organizationIsNotTheConsumer(unsafeBrandId(purposeSeed.consumerId)) @@ -288,7 +307,10 @@ export const testCreatePurpose = (): ReturnType => }); it("should throw riskAnalysisValidationFailed if the purpose has a non valid risk analysis ", async () => { await writeInReadmodel(tenant, tenants); - await writeInReadmodel(agreementEservice1, agreements); + await writeInReadmodel( + toReadModelAgreement(agreementEservice1), + agreements + ); await writeInReadmodel(toReadModelEService(eService1), eservices); const mockInvalidRiskAnalysisForm: RiskAnalysisForm = { @@ -307,7 +329,8 @@ export const testCreatePurpose = (): ReturnType => purposeService.createPurpose( seed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError( riskAnalysisValidationFailed([ @@ -325,14 +348,18 @@ export const testCreatePurpose = (): ReturnType => await addOnePurpose(existingPurpose, postgresDB, purposes); await writeInReadmodel(tenant, tenants); - await writeInReadmodel(agreementEservice1, agreements); + await writeInReadmodel( + toReadModelAgreement(agreementEservice1), + agreements + ); await writeInReadmodel(toReadModelEService(eService1), eservices); expect( purposeService.createPurpose( purposeSeed, unsafeBrandId(purposeSeed.consumerId), - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(duplicatedPurposeTitle(purposeSeed.title)); }); From 36872fc982d1defc0227533d6b6ca8030da4d349 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 May 2024 17:52:59 +0200 Subject: [PATCH 490/537] Revert renaming --- packages/purpose-process/open-api/purpose-service-spec.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/open-api/purpose-service-spec.yml b/packages/purpose-process/open-api/purpose-service-spec.yml index 8fd75f6ae9..ebfdd8cc7f 100644 --- a/packages/purpose-process/open-api/purpose-service-spec.yml +++ b/packages/purpose-process/open-api/purpose-service-spec.yml @@ -156,13 +156,13 @@ paths: - $ref: "#/components/parameters/CorrelationIdHeader" tags: - purpose - operationId: createReversePurpose + operationId: createPurposeFromEService description: Create a Purpose for EService with Receive mode requestBody: content: application/json: schema: - $ref: "#/components/schemas/ReversePurposeSeed" + $ref: "#/components/schemas/EServicePurposeSeed" required: true responses: "200": @@ -1206,7 +1206,7 @@ components: format: uuid required: - eserviceId - ReversePurposeSeed: + EServicePurposeSeed: type: object description: contains the expected payload for purpose creation. properties: From ca4023af7864885ab4020e0547c1a38871475643 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 May 2024 09:32:14 +0200 Subject: [PATCH 491/537] Minor fix --- .../src/models/purpose/purposeEventNotificationMappers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts index e93dd0a944..803c5a26ba 100644 --- a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMappers.ts @@ -20,7 +20,7 @@ export const toPurposeVersionStateV1Notification = ( .with(purposeVersionState.active, () => "Active") .with(purposeVersionState.suspended, () => "Suspended") .with(purposeVersionState.archived, () => "Archived") - .with(purposeVersionState.waitingForApproval, () => "Waiting for approval") + .with(purposeVersionState.waitingForApproval, () => "WaitingForApproval") .with(purposeVersionState.rejected, () => "Rejected") .exhaustive(); From a178eaee2b68528a1dff4c2975cf17266cfc01e9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 May 2024 09:34:31 +0200 Subject: [PATCH 492/537] Minor fixes --- .../src/models/purpose/purposeEventNotificationMessage.ts | 7 ++----- .../test/notificationMessage.integration.test.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts index a728240774..a426ec90ee 100644 --- a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts @@ -20,6 +20,8 @@ export const eventV2TypeMapper = ( "PurposeActivated", "NewPurposeVersionActivated", "PurposeVersionActivated", + "PurposeVersionUnsuspendedByProducer", + "PurposeVersionUnsuspendedByConsumer", () => "purpose_version_activated" ) .with( @@ -27,11 +29,6 @@ export const eventV2TypeMapper = ( "WaitingForApprovalPurposeDeleted", () => "purpose_deleted" ) - .with( - "PurposeVersionUnsuspendedByProducer", - "PurposeVersionUnsuspendedByConsumer", - () => "purpose_version_activated" - ) .with( "PurposeVersionSuspendedByProducer", "PurposeVersionSuspendedByConsumer", diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 9e8bc7b18e..21b4c078f8 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -1,7 +1,7 @@ /* eslint-disable functional/immutable-data */ /* eslint-disable functional/no-let */ import { StartedTestContainer } from "testcontainers"; -import { afterAll, assert, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { EServiceDescriptorSuspendedV2, From 71bbdbc0449462e31819a3bac61512d873bf879a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 May 2024 10:25:54 +0200 Subject: [PATCH 493/537] Fix --- packages/purpose-process/src/services/purposeService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index b946907219..1e57f737f1 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -3,7 +3,6 @@ import { DB, Logger, eventRepository, - logger, riskAnalysisFormToRiskAnalysisFormToValidate, } from "pagopa-interop-commons"; import { @@ -684,7 +683,7 @@ export function purposeServiceBuilder( assertOrganizationIsAConsumer(organizationId, consumerId); const eservice = await retrieveEService(eserviceId, readModelService); - assertEserviceHasSpecificMode(eservice, eserviceMode.receive); + assertEserviceMode(eservice, eserviceMode.receive); const riskAnalysis = retrieveRiskAnalysis( unsafeBrandId(seed.riskAnalysisId), @@ -705,7 +704,7 @@ export function purposeServiceBuilder( await retrieveActiveAgreement(eserviceId, consumerId, readModelService); - const purposeWithSameName = await readModelService.getSpecificPurpose( + const purposeWithSameName = await readModelService.getPurpose( eserviceId, consumerId, seed.title @@ -886,6 +885,7 @@ const performUpdatePurpose = async ( mode === eserviceMode.deliver ? validateAndTransformRiskAnalysis( updateContent.riskAnalysisForm, + true, tenant.kind ) : reverseValidateAndTransformRiskAnalysis( From f69879e9e792ac1d9be0629d67006d5654e3882d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 May 2024 10:26:48 +0200 Subject: [PATCH 494/537] Fix --- packages/purpose-process/src/model/domain/models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/models.ts b/packages/purpose-process/src/model/domain/models.ts index dd88bebfbb..497d38b8f4 100644 --- a/packages/purpose-process/src/model/domain/models.ts +++ b/packages/purpose-process/src/model/domain/models.ts @@ -26,5 +26,5 @@ export type ApiReversePurposeUpdateContent = z.infer< >; export type ApiReversePurposeSeed = z.infer< - typeof api.schemas.ReversePurposeSeed + typeof api.schemas.EServicePurposeSeed >; From f4487e1b7f17f8b2b2183a761d97340b16992065 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 May 2024 10:28:36 +0200 Subject: [PATCH 495/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 9fc60a9b75..6ae7ae91ff 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -150,12 +150,14 @@ const purposeRouter = ( "/reverse/purposes", authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = await purposeService.createReversePurpose( req.ctx.authData.organizationId, req.body, - req.ctx.correlationId + ctx.correlationId, + ctx.logger ); return res .status(200) @@ -164,7 +166,8 @@ const purposeRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - createReversePurposeErrorMapper + createReversePurposeErrorMapper, + ctx.logger ); return res.status(errorRes.status).json(errorRes).end(); } From ec9050440998ea3303d308733295984fcd5c2017 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 May 2024 10:43:17 +0200 Subject: [PATCH 496/537] Fix --- .../test/testCreateReversePurpose.ts | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index cec09058a8..546d91b4d5 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -27,10 +27,15 @@ import { purposeVersionState, tenantKind, toPurposeV2, + toReadModelAgreement, toReadModelEService, + toReadModelPurpose, unsafeBrandId, } from "pagopa-interop-models"; -import { unexpectedRulesVersionError } from "pagopa-interop-commons"; +import { + genericLogger, + unexpectedRulesVersionError, +} from "pagopa-interop-commons"; import { ApiReversePurposeSeed } from "../src/model/domain/models.js"; import { agreementNotFound, @@ -98,12 +103,13 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); const { purpose } = await purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ); const writtenEvent = await readLastEventByStreamId( @@ -190,13 +196,14 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( producer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(organizationIsNotTheConsumer(producer.id)); }); @@ -241,13 +248,14 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError( eServiceModeNotAllowed(mockEService.id, eserviceMode.receive) @@ -294,13 +302,14 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError( eserviceRiskAnalysisNotFound(mockEService.id, randomRiskAnalysisId) @@ -347,13 +356,14 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(missingFreeOfChargeReason()); }); @@ -398,13 +408,14 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(tenantKindNotFound(producer.id)); }); @@ -447,7 +458,8 @@ export const testCreateReversePurpose = (): ReturnType => purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(agreementNotFound(mockEService.id, consumer.id)); }); @@ -497,17 +509,18 @@ export const testCreateReversePurpose = (): ReturnType => dailyCalls: 1, }; - await writeInReadmodel(mockPurpose, purposes); + await writeInReadmodel(toReadModelPurpose(mockPurpose), purposes); await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError(duplicatedPurposeTitle(purposeTitle)); }); @@ -560,13 +573,14 @@ export const testCreateReversePurpose = (): ReturnType => await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(producer, tenants); await writeInReadmodel(consumer, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.createReversePurpose( consumer.id, reversePurposeSeed, - generateId() + generateId(), + genericLogger ) ).rejects.toThrowError( riskAnalysisValidationFailed([ From 9e5603c20f3d24e869a7fd54f9f795affcbf6145 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Fri, 10 May 2024 12:47:35 +0200 Subject: [PATCH 497/537] fix --- packages/purpose-process/src/model/domain/toEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/model/domain/toEvent.ts b/packages/purpose-process/src/model/domain/toEvent.ts index f516f95f21..426c0e19c1 100644 --- a/packages/purpose-process/src/model/domain/toEvent.ts +++ b/packages/purpose-process/src/model/domain/toEvent.ts @@ -173,7 +173,7 @@ export const toCreateEventPurposeSuspendedByProducer = ({ export function toCreateEventPurposeAdded( purpose: Purpose, correlationId: string -): CreateEvent { +): CreateEvent { return { streamId: purpose.id, version: 0, From 515183bc0f715f0933d25e7fed279b90d957e948 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Fri, 10 May 2024 14:11:11 +0200 Subject: [PATCH 498/537] fix as suggest --- package.json | 3 --- packages/purpose-process/src/model/domain/errors.ts | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/package.json b/package.json index 3a86e3f6f5..8bd1839e8d 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,5 @@ "@tsconfig/node-lts": "18.12.5", "turbo": "1.10.7" }, - "config": { - "protocVersion": "26.1" - }, "packageManager": "pnpm@8.6.3" } diff --git a/packages/purpose-process/src/model/domain/errors.ts b/packages/purpose-process/src/model/domain/errors.ts index 6a55f8c6f6..c12a491af3 100644 --- a/packages/purpose-process/src/model/domain/errors.ts +++ b/packages/purpose-process/src/model/domain/errors.ts @@ -29,8 +29,7 @@ export const errorCodes = { purposeNotInDraftState: "0015", duplicatedPurposeTitle: "0016", purposeCannotBeDeleted: "0017", - missingRejectionReason: "0018", - agreementNotFound: "0019", + agreementNotFound: "0018", }; export type ErrorCodes = keyof typeof errorCodes; From a7fe2d5968f36889e00aa5fd4db6f58fe8ccc8d4 Mon Sep 17 00:00:00 2001 From: Roberto Gregnanin Date: Fri, 10 May 2024 17:58:51 +0200 Subject: [PATCH 499/537] fix as suggest --- package.json | 3 +++ packages/models/package.json | 3 --- packages/purpose-process/src/utilities/errorMappers.ts | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 8bd1839e8d..e12af53002 100644 --- a/package.json +++ b/package.json @@ -35,5 +35,8 @@ "@tsconfig/node-lts": "18.12.5", "turbo": "1.10.7" }, + "config": { + "protocVersion": "26.1" + }, "packageManager": "pnpm@8.6.3" } diff --git a/packages/models/package.json b/packages/models/package.json index 110978d119..1bed795e11 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -8,9 +8,6 @@ "exports": { ".": "./dist/index.js" }, - "config": { - "protocVersion": "26.1" - }, "scripts": { "lint": "eslint . --ext .ts,.tsx", "lint:autofix": "eslint . --ext .ts,.tsx --fix", diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 70ffbd0b29..064d1bd940 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -112,10 +112,8 @@ export const suspendPurposeVersionErrorMapper = ( export const createPurposeErrorMapper = (error: ApiError): number => match(error.code) .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) - .with("missingFreeOfChargeReason", () => HTTP_STATUS_NOT_FOUND) + .with("missingFreeOfChargeReason", () => HTTP_STATUS_BAD_REQUEST) .with("agreementNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("tenantNotFound", () => HTTP_STATUS_BAD_REQUEST) - .with("tenantKindNotFound", () => HTTP_STATUS_BAD_REQUEST) .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); From a253911229bdf27da530b26c1b936af9150319d4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 May 2024 12:07:16 +0200 Subject: [PATCH 500/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 8 +++++++- packages/purpose-process/src/services/purposeService.ts | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 8a51eef9c3..9bbcc6fd9c 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -375,6 +375,7 @@ const purposeRouter = ( "/purposes/:purposeId/clone", authorizationMiddleware([ADMIN_ROLE]), async (req, res) => { + const ctx = fromAppContext(req.ctx); try { const { purpose, isRiskAnalysisValid } = await purposeService.clonePurpose({ @@ -382,13 +383,18 @@ const purposeRouter = ( organizationId: req.ctx.authData.organizationId, seed: req.body, correlationId: req.ctx.correlationId, + logger: ctx.logger, }); return res .status(200) .json(purposeToApiPurpose(purpose, isRiskAnalysisValid)) .end(); } catch (error) { - const errorRes = makeApiProblem(error, clonePurposeErrorMapper); + const errorRes = makeApiProblem( + error, + clonePurposeErrorMapper, + ctx.logger + ); return res.status(errorRes.status).json(errorRes).end(); } } diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index f7eba4d7f9..f933749504 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -4,7 +4,6 @@ import { Logger, eventRepository, formatDateAndTime, - logger, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, } from "pagopa-interop-commons"; @@ -755,11 +754,13 @@ export function purposeServiceBuilder( organizationId, seed, correlationId, + logger, }: { purposeId: PurposeId; organizationId: TenantId; seed: ApiPurposeCloneSeed; correlationId: string; + logger: Logger; }): Promise<{ purpose: Purpose; isRiskAnalysisValid: boolean }> { logger.info(`Cloning Purpose ${purposeId}`); @@ -809,7 +810,7 @@ export function purposeServiceBuilder( purposeToClone.data.title } - clone - ${formatDateAndTime(currentDate)}`; - const purposeWithSameName = await readModelService.getSpecificPurpose( + const purposeWithSameName = await readModelService.getPurpose( unsafeBrandId(seed.eserviceId), organizationId, clonedPurposeName From 7c27520ee1145367035c3d3f0936cefe315050ab Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 May 2024 12:10:10 +0200 Subject: [PATCH 501/537] Fix test --- .../purpose-process/test/testClonePurpose.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts index 6f25d5f84d..a2eaffb017 100644 --- a/packages/purpose-process/test/testClonePurpose.ts +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -18,10 +18,11 @@ import { purposeVersionState, tenantKind, toPurposeV2, + toReadModelAgreement, unsafeBrandId, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; -import { formatDateAndTime } from "pagopa-interop-commons"; +import { formatDateAndTime, genericLogger } from "pagopa-interop-commons"; import { duplicatedPurposeTitle, purposeCannotBeCloned, @@ -71,7 +72,7 @@ export const testClonePurpose = (): ReturnType => await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); const { purpose } = await purposeService.clonePurpose({ purposeId: mockPurpose.id, @@ -80,6 +81,7 @@ export const testClonePurpose = (): ReturnType => eserviceId: mockEService.id, }, correlationId: generateId(), + logger: genericLogger, }); const writtenEvent = await readLastEventByStreamId( @@ -142,7 +144,7 @@ export const testClonePurpose = (): ReturnType => }; await writeInReadmodel(mockTenant, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.clonePurpose({ @@ -152,6 +154,7 @@ export const testClonePurpose = (): ReturnType => eserviceId: mockEService.id, }, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeNotFound(mockPurpose.id)); }); @@ -177,7 +180,7 @@ export const testClonePurpose = (): ReturnType => await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.clonePurpose({ @@ -187,6 +190,7 @@ export const testClonePurpose = (): ReturnType => eserviceId: mockEService.id, }, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeCannotBeCloned(mockPurpose.id)); }); @@ -212,7 +216,7 @@ export const testClonePurpose = (): ReturnType => await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.clonePurpose({ @@ -222,6 +226,7 @@ export const testClonePurpose = (): ReturnType => eserviceId: mockEService.id, }, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(purposeCannotBeCloned(mockPurpose.id)); }); @@ -257,7 +262,7 @@ export const testClonePurpose = (): ReturnType => await addOnePurpose(mockPurposeToClone, postgresDB, purposes); await addOnePurpose(mockPurposeWithSameName, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.clonePurpose({ @@ -267,6 +272,7 @@ export const testClonePurpose = (): ReturnType => eserviceId: mockEService.id, }, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError( duplicatedPurposeTitle(mockPurposeWithSameName.title) @@ -294,7 +300,7 @@ export const testClonePurpose = (): ReturnType => await addOnePurpose(mockPurpose, postgresDB, purposes); await writeInReadmodel(mockTenant, tenants); - await writeInReadmodel(mockAgreement, agreements); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); expect( purposeService.clonePurpose({ @@ -304,6 +310,7 @@ export const testClonePurpose = (): ReturnType => eserviceId: mockEService.id, }, correlationId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); From 278398db0cb72fdcc1d87a780d00ce30005cf837 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 13 May 2024 14:08:55 +0200 Subject: [PATCH 502/537] fix tests --- .../test/testGetRiskAnalysisConfigurationByVersion.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts index ba9223d25e..92d0932a0e 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts @@ -14,7 +14,7 @@ import { toReadModelEService, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; -import { getFormRulesByVersion } from "pagopa-interop-commons"; +import { genericLogger, getFormRulesByVersion } from "pagopa-interop-commons"; import { riskAnalysisConfigVersionNotFound, eserviceNotFound, @@ -47,6 +47,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< eserviceId: mockEservice.id, riskAnalysisVersion, organizationId: mockTenant.id, + logger: genericLogger, }); expect(result).toEqual(getFormRulesByVersion(kind, riskAnalysisVersion)); @@ -68,6 +69,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< eserviceId: mockEservice.id, riskAnalysisVersion, organizationId: mockTenant.id, + logger: genericLogger, }); expect(result).toEqual(getFormRulesByVersion(kind, riskAnalysisVersion)); @@ -82,6 +84,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< eserviceId: randomId, riskAnalysisVersion: "1.0", organizationId: mockTenant.id, + logger: genericLogger, }) ).rejects.toThrowError(eserviceNotFound(randomId)); }); @@ -95,6 +98,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< eserviceId: mockEservice.id, riskAnalysisVersion: "1.0", organizationId: randomId, + logger: genericLogger, }) ).rejects.toThrowError(tenantNotFound(randomId)); }); @@ -112,6 +116,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< eserviceId: mockEservice.id, riskAnalysisVersion: "1.0", organizationId: mockTenant.id, + logger: genericLogger, }) ).rejects.toThrowError(tenantKindNotFound(mockTenant.id)); }); @@ -131,6 +136,7 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< eserviceId: mockEservice.id, riskAnalysisVersion: wrongRiskAnalysisVersion, organizationId: mockTenant.id, + logger: genericLogger, }) ).rejects.toThrowError( riskAnalysisConfigVersionNotFound( From 322669ad2227e6b72cc42efff35dcdc715e92da2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 May 2024 15:02:16 +0200 Subject: [PATCH 503/537] Fix --- packages/purpose-process/src/routers/PurposeRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/routers/PurposeRouter.ts b/packages/purpose-process/src/routers/PurposeRouter.ts index 6ae7ae91ff..ce587ae526 100644 --- a/packages/purpose-process/src/routers/PurposeRouter.ts +++ b/packages/purpose-process/src/routers/PurposeRouter.ts @@ -94,7 +94,7 @@ const purposeRouter = ( const purposes = await purposeService.getPurposes( req.ctx.authData.organizationId, { - name, + title: name, eservicesIds: eservicesIds?.map(unsafeBrandId), consumersIds: consumersIds?.map(unsafeBrandId), producersIds: producersIds?.map(unsafeBrandId), From 824cc6dc538b2483506ab5bb857ce9b896e6004a Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 May 2024 15:32:01 +0200 Subject: [PATCH 504/537] Fix --- .../test/notificationMessage.integration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 9e8bc7b18e..21b4c078f8 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -1,7 +1,7 @@ /* eslint-disable functional/immutable-data */ /* eslint-disable functional/no-let */ import { StartedTestContainer } from "testcontainers"; -import { afterAll, assert, beforeAll, describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { EServiceDescriptorSuspendedV2, From 34c6c5ae4cea72956496eabc708a1cb7f2d6fab1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 May 2024 16:58:18 +0200 Subject: [PATCH 505/537] Fix --- packages/notifier-seeder/src/index.ts | 3 +-- .../test/notificationMessage.integration.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/notifier-seeder/src/index.ts b/packages/notifier-seeder/src/index.ts index 3f38dd623a..11d236fdd1 100644 --- a/packages/notifier-seeder/src/index.ts +++ b/packages/notifier-seeder/src/index.ts @@ -11,7 +11,6 @@ import { logger, loggerConfig, purposeTopicConfig, - runWithContext, } from "pagopa-interop-commons"; import { match } from "ts-pattern"; import { EServiceEventV2, PurposeEventV2 } from "pagopa-interop-models"; @@ -77,7 +76,7 @@ export function processMessage( return; } - await queueManager.send(message); + await queueManager.send(message, loggerInstance); loggerInstance.info( `Notification message [${message.messageUUID}] sent to queue ${queueConfig.queueUrl} for event type "${decodedMessage.type}"` diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 1138b3661f..53b249f9ab 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -163,7 +163,7 @@ describe("Notification tests", async () => { catalogEventEnvelope, CatalogItemEventNotification ); - await queueWriter.send(catalogMessage); + await queueWriter.send(catalogMessage, genericLogger); const mockPurpose = getMockPurpose(); @@ -189,9 +189,9 @@ describe("Notification tests", async () => { purposeEventNotification ); - await queueWriter.send(purposeMessage); + await queueWriter.send(purposeMessage, genericLogger); - const receivedMessages = await queueWriter.receiveLast(2); + const receivedMessages = await queueWriter.receiveLast(genericLogger, 2); expect(receivedMessages.length).toBe(2); const receivedCatalogMessage = receivedMessages[0]; From 56d88e41b06a613b76ca1f211afc34c68b43997f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 14 May 2024 12:12:38 +0200 Subject: [PATCH 506/537] Fix --- packages/models/src/purpose/purposeReadModelAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/purpose/purposeReadModelAdapter.ts b/packages/models/src/purpose/purposeReadModelAdapter.ts index c6ba3892ad..1f22f18609 100644 --- a/packages/models/src/purpose/purposeReadModelAdapter.ts +++ b/packages/models/src/purpose/purposeReadModelAdapter.ts @@ -20,7 +20,7 @@ export const toReadModelPurposeVersion = ( updatedAt: purposeVersion.updatedAt?.toISOString(), firstActivationAt: purposeVersion.firstActivationAt?.toISOString(), suspendedAt: purposeVersion.suspendedAt?.toISOString(), - expectedApprovalDate: purposeVersion.expectedApprovalDate?.toISOString(), + expectedApprovalDate: undefined, riskAnalysis: purposeVersion.riskAnalysis ? toReadModelPurposeVersionDocument(purposeVersion.riskAnalysis) : undefined, From 46997c991e91ee1d0fe492d53a74490a7b5c66e4 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 14 May 2024 15:15:25 +0200 Subject: [PATCH 507/537] Fix and refactor --- .../src/services/purposeService.ts | 10 +++---- .../src/services/validators.ts | 28 +++++++++++-------- .../src/utilities/errorMappers.ts | 7 +---- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index e2f7f36b7a..921a7d5211 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -703,13 +703,13 @@ export function purposeServiceBuilder( throw duplicatedPurposeTitle(seed.title); } - validateRiskAnalysisOrThrow( - riskAnalysisFormToRiskAnalysisFormToValidate( + validateRiskAnalysisOrThrow({ + riskAnalysisForm: riskAnalysisFormToRiskAnalysisFormToValidate( riskAnalysis.riskAnalysisForm ), - false, - producer.kind - ); + schemaOnlyValidation: false, + tenantKind: producer.kind, + }); const newVersion: PurposeVersion = { id: generateId(), diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index 1da6a20b6a..a120099779 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -84,11 +84,15 @@ export const assertOrganizationIsAConsumer = ( } }; -export function validateRiskAnalysisOrThrow( - riskAnalysisForm: ApiRiskAnalysisFormSeed, - schemaOnlyValidation: boolean, - tenantKind: TenantKind -): RiskAnalysisValidatedForm { +export function validateRiskAnalysisOrThrow({ + riskAnalysisForm, + schemaOnlyValidation, + tenantKind, +}: { + riskAnalysisForm: ApiRiskAnalysisFormSeed; + schemaOnlyValidation: boolean; + tenantKind: TenantKind; +}): RiskAnalysisValidatedForm { const result = validateRiskAnalysis( riskAnalysisForm, schemaOnlyValidation, @@ -109,11 +113,11 @@ export function validateAndTransformRiskAnalysis( if (!riskAnalysisForm) { return undefined; } - const validatedForm = validateRiskAnalysisOrThrow( + const validatedForm = validateRiskAnalysisOrThrow({ riskAnalysisForm, schemaOnlyValidation, - tenantKind - ); + tenantKind, + }); return { ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), @@ -132,11 +136,11 @@ export function reverseValidateAndTransformRiskAnalysis( const formToValidate = riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm); - const validatedForm = validateRiskAnalysisOrThrow( - formToValidate, + const validatedForm = validateRiskAnalysisOrThrow({ + riskAnalysisForm: formToValidate, schemaOnlyValidation, - tenantKind - ); + tenantKind, + }); return { ...riskAnalysisValidatedFormToNewRiskAnalysisForm(validatedForm), diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 072bc3366a..1b6fdba010 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -122,11 +122,7 @@ export const createReversePurposeErrorMapper = ( error: ApiError ): number => match(error.code) - .with( - "organizationIsNotTheConsumer", - "tenantKindNotFound", - () => HTTP_STATUS_FORBIDDEN - ) + .with("organizationIsNotTheConsumer", () => HTTP_STATUS_FORBIDDEN) .with( "eserviceNotFound", "eServiceModeNotAllowed", @@ -134,7 +130,6 @@ export const createReversePurposeErrorMapper = ( "missingFreeOfChargeReason", "agreementNotFound", "riskAnalysisValidationFailed", - "tenantNotFound", () => HTTP_STATUS_BAD_REQUEST ) .with("duplicatedPurposeTitle", () => HTTP_STATUS_CONFLICT) From 311556c9bc5760598a640d507b164db8c58e1a05 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 14 May 2024 15:18:18 +0200 Subject: [PATCH 508/537] Fix typo --- packages/purpose-process/test/testCreateReversePurpose.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/test/testCreateReversePurpose.ts b/packages/purpose-process/test/testCreateReversePurpose.ts index 546d91b4d5..4a86e9ab7d 100644 --- a/packages/purpose-process/test/testCreateReversePurpose.ts +++ b/packages/purpose-process/test/testCreateReversePurpose.ts @@ -463,7 +463,7 @@ export const testCreateReversePurpose = (): ReturnType => ) ).rejects.toThrowError(agreementNotFound(mockEService.id, consumer.id)); }); - it("should throw duplicatePurposeTitle if a purpose with the same name already exists", async () => { + it("should throw duplicatedPurposeTitle if a purpose with the same name already exists", async () => { const consumer = getMockTenant(); const producer: Tenant = { ...getMockTenant(), kind: tenantKind.PA }; From 68420d0629610363fd068f5f9ee42b9da869dd49 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 14 May 2024 15:25:00 +0200 Subject: [PATCH 509/537] Refactor --- .../src/services/purposeService.ts | 39 +++++++------------ .../src/services/validators.ts | 25 ++++++++++++ 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 921a7d5211..229b5b0556 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -35,7 +35,6 @@ import { import { match } from "ts-pattern"; import { agreementNotFound, - duplicatedPurposeTitle, eserviceNotFound, eserviceRiskAnalysisNotFound, notValidVersionState, @@ -83,6 +82,7 @@ import { isArchivable, isSuspendable, validateRiskAnalysisOrThrow, + assertPurposeTitleIsNotDuplicated, } from "./validators.js"; const retrievePurpose = async ( @@ -626,15 +626,12 @@ export function purposeServiceBuilder( await retrieveActiveAgreement(eserviceId, consumerId, readModelService); - const purposeWithSameName = await readModelService.getPurpose( + await assertPurposeTitleIsNotDuplicated({ + readModelService, eserviceId, consumerId, - purposeSeed.title - ); - - if (purposeWithSameName) { - throw duplicatedPurposeTitle(purposeSeed.title); - } + title: purposeSeed.title, + }); const purpose: Purpose = { ...purposeSeed, @@ -693,15 +690,12 @@ export function purposeServiceBuilder( await retrieveActiveAgreement(eserviceId, consumerId, readModelService); - const purposeWithSameName = await readModelService.getPurpose( + await assertPurposeTitleIsNotDuplicated({ + readModelService, eserviceId, consumerId, - seed.title - ); - - if (purposeWithSameName) { - throw duplicatedPurposeTitle(seed.title); - } + title: seed.title, + }); validateRiskAnalysisOrThrow({ riskAnalysisForm: riskAnalysisFormToRiskAnalysisFormToValidate( @@ -842,15 +836,12 @@ const performUpdatePurpose = async ( assertPurposeIsDraft(purpose.data); if (updateContent.title !== purpose.data.title) { - const purposeWithSameTitle = await readModelService.getPurpose( - purpose.data.eserviceId, - purpose.data.consumerId, - updateContent.title - ); - - if (purposeWithSameTitle) { - throw duplicatedPurposeTitle(updateContent.title); - } + await assertPurposeTitleIsNotDuplicated({ + readModelService, + eserviceId: purpose.data.eserviceId, + consumerId: purpose.data.consumerId, + title: updateContent.title, + }); } const eservice = await retrieveEService( purpose.data.eserviceId, diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index a120099779..a260afadfa 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -9,6 +9,7 @@ import { TenantId, TenantKind, purposeVersionState, + EServiceId, } from "pagopa-interop-models"; import { validateRiskAnalysis, @@ -17,6 +18,7 @@ import { riskAnalysisValidatedFormToNewRiskAnalysisForm, } from "pagopa-interop-commons"; import { + duplicatedPurposeTitle, eServiceModeNotAllowed, missingFreeOfChargeReason, organizationIsNotTheConsumer, @@ -25,6 +27,7 @@ import { tenantKindNotFound, } from "../model/domain/errors.js"; import { ApiRiskAnalysisFormSeed } from "../model/domain/models.js"; +import { ReadModelService } from "./readModelService.js"; export const isRiskAnalysisFormValid = ( riskAnalysisForm: RiskAnalysisForm | undefined, @@ -176,3 +179,25 @@ export const isArchivable = (purposeVersion: PurposeVersion): boolean => export const isSuspendable = (purposeVersion: PurposeVersion): boolean => purposeVersion.state === purposeVersionState.active || purposeVersion.state === purposeVersionState.suspended; + +export const assertPurposeTitleIsNotDuplicated = async ({ + readModelService, + eserviceId, + consumerId, + title, +}: { + readModelService: ReadModelService; + eserviceId: EServiceId; + consumerId: TenantId; + title: string; +}): Promise => { + const purposeWithSameName = await readModelService.getPurpose( + eserviceId, + consumerId, + title + ); + + if (purposeWithSameName) { + throw duplicatedPurposeTitle(title); + } +}; From a84ae3f56dc34bbfdd3ef66ea0980754595dda13 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 15 May 2024 09:26:10 +0200 Subject: [PATCH 510/537] refactor tests --- ...stGetRiskAnalysisConfigurationByVersion.ts | 91 +++++++++++-------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts index 92d0932a0e..37be704630 100644 --- a/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts +++ b/packages/purpose-process/test/testGetRiskAnalysisConfigurationByVersion.ts @@ -2,7 +2,6 @@ import { getMockEService, getMockTenant, - randomArrayItem, writeInReadmodel, } from "pagopa-interop-commons-test/index.js"; import { @@ -30,50 +29,64 @@ export const testGetRiskAnalysisConfigurationByVersion = (): ReturnType< typeof describe > => describe("retrieveRiskAnalysisConfigurationByVersion", async () => { - it("should retrieve risk analysis configuration by version (Eservice mode: deliver)", async () => { - const mockEservice = { ...getMockEService(), mode: eserviceMode.deliver }; - const kind = randomArrayItem(Object.values(tenantKind)); - const mockTenant = { - ...getMockTenant(), - kind, - }; - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(mockTenant, tenants); + it.each(Object.values(tenantKind))( + "should retrieve risk analysis configuration by version from the consumer (Eservice mode: deliver) with kind %s", + async (kind) => { + const mockEservice = { + ...getMockEService(), + mode: eserviceMode.deliver, + }; + const mockTenant = { + ...getMockTenant(), + kind, + }; + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(mockTenant, tenants); - const riskAnalysisVersion = "1.0"; + const riskAnalysisVersion = "1.0"; - const result = - await purposeService.retrieveRiskAnalysisConfigurationByVersion({ - eserviceId: mockEservice.id, - riskAnalysisVersion, - organizationId: mockTenant.id, - logger: genericLogger, - }); + const result = + await purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion, + organizationId: mockTenant.id, + logger: genericLogger, + }); - expect(result).toEqual(getFormRulesByVersion(kind, riskAnalysisVersion)); - }); - it("should retrieve risk analysis configuration by version (Eservice mode: receive)", async () => { - const mockEservice = { ...getMockEService(), mode: eserviceMode.receive }; - const kind = randomArrayItem(Object.values(tenantKind)); - const mockTenant = { - ...getMockTenant(mockEservice.producerId), - kind, - }; - await writeInReadmodel(toReadModelEService(mockEservice), eservices); - await writeInReadmodel(mockTenant, tenants); + expect(result).toEqual( + getFormRulesByVersion(kind, riskAnalysisVersion) + ); + } + ); + it.each(Object.values(tenantKind))( + "should retrieve risk analysis configuration by version from the producer (Eservice mode: receive) with kind %s", + async (kind) => { + const mockEservice = { + ...getMockEService(), + mode: eserviceMode.receive, + }; + const mockTenant = { + ...getMockTenant(mockEservice.producerId), + kind, + }; + await writeInReadmodel(toReadModelEService(mockEservice), eservices); + await writeInReadmodel(mockTenant, tenants); - const riskAnalysisVersion = "1.0"; + const riskAnalysisVersion = "1.0"; - const result = - await purposeService.retrieveRiskAnalysisConfigurationByVersion({ - eserviceId: mockEservice.id, - riskAnalysisVersion, - organizationId: mockTenant.id, - logger: genericLogger, - }); + const result = + await purposeService.retrieveRiskAnalysisConfigurationByVersion({ + eserviceId: mockEservice.id, + riskAnalysisVersion, + organizationId: mockTenant.id, + logger: genericLogger, + }); - expect(result).toEqual(getFormRulesByVersion(kind, riskAnalysisVersion)); - }); + expect(result).toEqual( + getFormRulesByVersion(kind, riskAnalysisVersion) + ); + } + ); it("should throw eserviceNotFound if the eservice doesn't exist", async () => { const mockTenant = getMockTenant(); const randomId = generateId(); From e1f072ec69da655eb66d3fdf713f51c640bfba8d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 15 May 2024 12:44:52 +0200 Subject: [PATCH 511/537] Improve title logic --- .../src/services/purposeService.ts | 15 ++-- .../purpose-process/test/testClonePurpose.ts | 78 +++++++++++++++++++ 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 1c3aeffa21..9c2b24ddae 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -795,19 +795,24 @@ export function purposeServiceBuilder( : undefined; const currentDate = new Date(); - const clonedPurposeName = `${ - purposeToClone.data.title - } - clone - ${formatDateAndTime(currentDate)}`; + const title = purposeToClone.data.title; + const suffix = ` - clone - ${formatDateAndTime(currentDate)}`; + const prefixLengthAllowance = 60 - suffix.length - 3; + + const clonedPurposeTitle = + title.length + suffix.length <= 60 + ? `${title}${suffix}` + : `${title.slice(0, prefixLengthAllowance)}...${suffix}`; await assertPurposeTitleIsNotDuplicated({ readModelService, eserviceId: unsafeBrandId(seed.eserviceId), consumerId: organizationId, - title: clonedPurposeName, + title: clonedPurposeTitle, }); const clonedPurpose: Purpose = { - title: clonedPurposeName, + title: clonedPurposeTitle, id: generateId(), createdAt: currentDate, eserviceId: unsafeBrandId(seed.eserviceId), diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts index a2eaffb017..ae84fc13e1 100644 --- a/packages/purpose-process/test/testClonePurpose.ts +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -123,6 +123,84 @@ export const testClonePurpose = (): ReturnType => vi.useRealTimers(); }); + it("should write on event-store for the cloning of a purpose, making sure the title is cut to 60 characters", async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + const mockEService = getMockEService(); + + const mockAgreement = getMockAgreement( + mockEService.id, + mockTenant.id, + agreementState.active + ); + + const mockPurpose: Purpose = { + ...getMockPurpose(), + title: "Title exceeding the maximum length when the suffix is added", + eserviceId: mockEService.id, + consumerId: mockTenant.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + await addOnePurpose(mockPurpose, postgresDB, purposes); + await writeInReadmodel(mockTenant, tenants); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); + + const { purpose } = await purposeService.clonePurpose({ + purposeId: mockPurpose.id, + organizationId: mockTenant.id, + seed: { + eserviceId: mockEService.id, + }, + correlationId: generateId(), + logger: genericLogger, + }); + + const writtenEvent = await readLastEventByStreamId( + purpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: purpose.id, + version: "0", + type: "PurposeCloned", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeClonedV2, + payload: writtenEvent.data, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + id: unsafeBrandId(writtenPayload.purpose!.id), + title: `Title exceeding the maximum... - clone - ${formatDateAndTime( + new Date() + )}`, + versions: [ + { + id: unsafeBrandId(writtenPayload.purpose!.versions[0].id), + state: purposeVersionState.draft, + createdAt: new Date(), + dailyCalls: mockPurpose.versions[0].dailyCalls, + }, + ], + createdAt: new Date(), + }; + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + expect(expectedPurpose.title.length).toBe(60); + + vi.useRealTimers(); + }); it("should throw purposeNotFound if the purpose to clone doesn't exist", async () => { const mockTenant = { ...getMockTenant(), From a8898d0e0acf7c790ca716563aa985990edadba8 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 15 May 2024 14:28:39 +0200 Subject: [PATCH 512/537] Remove comment --- .../src/models/purpose/purposeEventNotification.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/notifier-seeder/src/models/purpose/purposeEventNotification.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotification.ts index 0acf2a9ee1..9f2cda1fc8 100644 --- a/packages/notifier-seeder/src/models/purpose/purposeEventNotification.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotification.ts @@ -49,16 +49,6 @@ export type PurposeNotification = { purpose: PurposeV1Notification; }; -/* -unused -// PurposeVersionCreatedV1 -// PurposeVersionUpdatedV1 -export type PurposeIdAndVersiondNotification = { - purposeId: string; - version: PurposeVersionV1Notification; -}; -*/ - // PurposeVersionRejectedV1 export type PurposeAndVersionIdNotification = { purpose: PurposeV1Notification; From ab11a82e4b144b6c02a3fe72f5676710da3bf028 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 15 May 2024 16:40:02 +0200 Subject: [PATCH 513/537] Add comment --- packages/purpose-process/src/services/purposeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 9c2b24ddae..4cb57750b7 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -798,7 +798,7 @@ export function purposeServiceBuilder( const title = purposeToClone.data.title; const suffix = ` - clone - ${formatDateAndTime(currentDate)}`; const prefixLengthAllowance = 60 - suffix.length - 3; - + // 60 is the maximum length for the purpose title, according to the api spec (PurposeSeed) const clonedPurposeTitle = title.length + suffix.length <= 60 ? `${title}${suffix}` From 5b4050185ef762a6a78707e268d4605340ea7085 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 May 2024 14:23:16 +0200 Subject: [PATCH 514/537] Remove unused error --- packages/purpose-process/src/utilities/errorMappers.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/purpose-process/src/utilities/errorMappers.ts b/packages/purpose-process/src/utilities/errorMappers.ts index 1d0873081f..827269f196 100644 --- a/packages/purpose-process/src/utilities/errorMappers.ts +++ b/packages/purpose-process/src/utilities/errorMappers.ts @@ -138,7 +138,6 @@ export const createReversePurposeErrorMapper = ( export const clonePurposeErrorMapper = (error: ApiError): number => match(error.code) .with("purposeNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("riskAnalysisValidationFailed", () => HTTP_STATUS_BAD_REQUEST) .with( "duplicatedPurposeTitle", "purposeCannotBeCloned", From 37c825c85d64ba787698f6579fc5efe192b04a80 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 May 2024 14:23:59 +0200 Subject: [PATCH 515/537] Remove redundant operations in test --- packages/purpose-process/test/testClonePurpose.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/purpose-process/test/testClonePurpose.ts b/packages/purpose-process/test/testClonePurpose.ts index ae84fc13e1..d5bc6e76ac 100644 --- a/packages/purpose-process/test/testClonePurpose.ts +++ b/packages/purpose-process/test/testClonePurpose.ts @@ -48,9 +48,6 @@ export const testClonePurpose = (): ReturnType => vi.useRealTimers(); }); it("should write on event-store for the cloning of a purpose", async () => { - vi.useFakeTimers(); - vi.setSystemTime(new Date()); - const mockTenant = { ...getMockTenant(), kind: tenantKind.PA, @@ -120,13 +117,8 @@ export const testClonePurpose = (): ReturnType => }; expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); - - vi.useRealTimers(); }); it("should write on event-store for the cloning of a purpose, making sure the title is cut to 60 characters", async () => { - vi.useFakeTimers(); - vi.setSystemTime(new Date()); - const mockTenant = { ...getMockTenant(), kind: tenantKind.PA, @@ -198,8 +190,6 @@ export const testClonePurpose = (): ReturnType => expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); expect(expectedPurpose.title.length).toBe(60); - - vi.useRealTimers(); }); it("should throw purposeNotFound if the purpose to clone doesn't exist", async () => { const mockTenant = { From 447c6cfb540b081b7bd833d387ac659e501a057e Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 17 May 2024 10:33:57 +0200 Subject: [PATCH 516/537] Scaffold --- packages/authorization-process/.env | 20 + packages/authorization-process/Dockerfile | 41 + .../kubernetes/dev/configmap.yaml | 8 + .../kubernetes/dev/deployment.yaml | 122 ++ .../kubernetes/dev/service.yaml | 14 + .../kubernetes/dev/serviceAccount.yaml | 9 + .../open-api/authorization-service-spec.yml | 1264 +++++++++++++++++ packages/authorization-process/package.json | 51 + packages/authorization-process/src/app.ts | 22 + packages/authorization-process/src/index.ts | 7 + .../src/model/domain/apiConverter.ts | 0 .../src/model/domain/errors.ts | 9 + .../src/model/domain/models.ts | 0 .../src/model/domain/toEvent.ts | 1 + .../src/routers/AuthorizationRouter.ts | 123 ++ .../src/routers/HealthRouter.ts | 8 + .../src/services/authorizationService.ts | 20 + .../src/services/readModelService.ts | 16 + .../src/utilities/config.ts | 15 + .../src/utilities/errorMappers.ts | 1 + .../authorization-process/test/.eslintrc.json | 7 + .../authorizationService.integration.test.ts | 9 + .../authorization-process/test/tsconfig.json | 4 + .../test/vitestGlobalSetup.ts | 3 + .../authorization-process/tsconfig.check.json | 7 + packages/authorization-process/tsconfig.json | 7 + .../authorization-process/vitest.config.ts | 11 + pnpm-lock.yaml | 319 ++--- 28 files changed, 1932 insertions(+), 186 deletions(-) create mode 100644 packages/authorization-process/.env create mode 100644 packages/authorization-process/Dockerfile create mode 100644 packages/authorization-process/kubernetes/dev/configmap.yaml create mode 100644 packages/authorization-process/kubernetes/dev/deployment.yaml create mode 100644 packages/authorization-process/kubernetes/dev/service.yaml create mode 100644 packages/authorization-process/kubernetes/dev/serviceAccount.yaml create mode 100644 packages/authorization-process/open-api/authorization-service-spec.yml create mode 100644 packages/authorization-process/package.json create mode 100644 packages/authorization-process/src/app.ts create mode 100644 packages/authorization-process/src/index.ts create mode 100644 packages/authorization-process/src/model/domain/apiConverter.ts create mode 100644 packages/authorization-process/src/model/domain/errors.ts create mode 100644 packages/authorization-process/src/model/domain/models.ts create mode 100644 packages/authorization-process/src/model/domain/toEvent.ts create mode 100644 packages/authorization-process/src/routers/AuthorizationRouter.ts create mode 100644 packages/authorization-process/src/routers/HealthRouter.ts create mode 100644 packages/authorization-process/src/services/authorizationService.ts create mode 100644 packages/authorization-process/src/services/readModelService.ts create mode 100644 packages/authorization-process/src/utilities/config.ts create mode 100644 packages/authorization-process/src/utilities/errorMappers.ts create mode 100644 packages/authorization-process/test/.eslintrc.json create mode 100644 packages/authorization-process/test/authorizationService.integration.test.ts create mode 100644 packages/authorization-process/test/tsconfig.json create mode 100644 packages/authorization-process/test/vitestGlobalSetup.ts create mode 100644 packages/authorization-process/tsconfig.check.json create mode 100644 packages/authorization-process/tsconfig.json create mode 100644 packages/authorization-process/vitest.config.ts diff --git a/packages/authorization-process/.env b/packages/authorization-process/.env new file mode 100644 index 0000000000..ea2ae91c36 --- /dev/null +++ b/packages/authorization-process/.env @@ -0,0 +1,20 @@ +HOST=0.0.0.0 +PORT=3000 +LOG_LEVEL=info + +EVENTSTORE_DB_HOST=localhost +EVENTSTORE_DB_NAME=root +EVENTSTORE_DB_USERNAME=root +EVENTSTORE_DB_PASSWORD=root +EVENTSTORE_DB_PORT=6001 +EVENTSTORE_DB_SCHEMA=authorization +EVENTSTORE_DB_USE_SSL=false + +READMODEL_DB_HOST=localhost +READMODEL_DB_NAME=readmodel +READMODEL_DB_USERNAME=root +READMODEL_DB_PASSWORD=example +READMODEL_DB_PORT=27017 + +WELL_KNOWN_URLS="https://dev.interop.pagopa.it/.well-known/jwks.json" +ACCEPTED_AUDIENCES="dev.interop.pagopa.it/ui,refactor.dev.interop.pagopa.it/ui" diff --git a/packages/authorization-process/Dockerfile b/packages/authorization-process/Dockerfile new file mode 100644 index 0000000000..7c3f358bf3 --- /dev/null +++ b/packages/authorization-process/Dockerfile @@ -0,0 +1,41 @@ +FROM node:18.20.2-slim@sha256:f2e7a19c91d98b854c226c04c4a1bf7d9d5fac28f320777efe5e01aa2e70c474 as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ + +COPY ./packages/authorization-process/package.json /app/packages/authorization-process/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json + +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile + +COPY tsconfig.json /app/ +COPY turbo.json /app/ +COPY ./packages/authorization-process /app/packages/authorization-process +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models + +RUN pnpm build && \ + rm -rf /app/node_modules/.modules.yaml && \ + rm -rf /app/node_modules/.cache && \ + mkdir /out && \ + cp -a --parents -t /out \ + node_modules packages/authorization-process/node_modules \ + package*.json packages/authorization-process/package*.json \ + packages/commons/ \ + packages/models/ \ + packages/authorization-process/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:18.20.2-slim@sha256:f2e7a19c91d98b854c226c04c4a1bf7d9d5fac28f320777efe5e01aa2e70c474 as final + +COPY --from=build /out /app + +WORKDIR /app/packages/authorization-process +EXPOSE 3000 + +CMD [ "node", "." ] diff --git a/packages/authorization-process/kubernetes/dev/configmap.yaml b/packages/authorization-process/kubernetes/dev/configmap.yaml new file mode 100644 index 0000000000..d7b34c6589 --- /dev/null +++ b/packages/authorization-process/kubernetes/dev/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: interop-be-authorization-process-refactor + namespace: dev-refactor +data: + EVENTSTORE_DB_USE_SSL: "true" + EVENTSTORE_DB_SCHEMA: "authorization" diff --git a/packages/authorization-process/kubernetes/dev/deployment.yaml b/packages/authorization-process/kubernetes/dev/deployment.yaml new file mode 100644 index 0000000000..afac93ebdb --- /dev/null +++ b/packages/authorization-process/kubernetes/dev/deployment.yaml @@ -0,0 +1,122 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: interop-be-authorization-process-refactor + namespace: dev-refactor + labels: + app: interop-be-authorization-process-refactor +spec: + replicas: 1 + selector: + matchLabels: + app: interop-be-authorization-process-refactor + template: + metadata: + labels: + app: interop-be-authorization-process-refactor + spec: + serviceAccountName: interop-be-authorization-process-refactor + containers: + - name: interop-be-authorization-process-refactor + image: ghcr.io/pagopa/authorization-process@$IMAGE_DIGEST + imagePullPolicy: Always + ports: + - name: http + containerPort: 3000 + protocol: TCP + resources: + requests: + cpu: 1.0 + memory: 2Gi + limits: + cpu: 1.0 + memory: 2Gi + livenessProbe: + httpGet: + path: /status + port: http + initialDelaySeconds: 15 + periodSeconds: 5 + readinessProbe: + httpGet: + path: /status + port: http + initialDelaySeconds: 15 + periodSeconds: 5 + env: + - name: PORT + value: "3000" + - name: HOST + value: "0.0.0.0" + - name: LOG_LEVEL + value: info + - name: EVENTSTORE_DB_HOST + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: EVENTSTORE_DB_HOST + - name: EVENTSTORE_DB_NAME + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: EVENTSTORE_DB_NAME + - name: EVENTSTORE_DB_PORT + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: EVENTSTORE_DB_PORT + - name: EVENTSTORE_DB_USERNAME + valueFrom: + secretKeyRef: + name: persistence-management + key: REFACTOR_USERNAME + - name: EVENTSTORE_DB_PASSWORD + valueFrom: + secretKeyRef: + name: persistence-management + key: REFACTOR_USER_PASSWORD + - name: EVENTSTORE_DB_SCHEMA + valueFrom: + configMapKeyRef: + name: interop-be-authorization-process-refactor + key: EVENTSTORE_DB_SCHEMA + - name: EVENTSTORE_DB_USE_SSL + valueFrom: + configMapKeyRef: + name: interop-be-authorization-process-refactor + key: EVENTSTORE_DB_USE_SSL + - name: READMODEL_DB_HOST + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_HOST + - name: READMODEL_DB_NAME + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_NAME + - name: READMODEL_DB_PORT + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: READMODEL_DB_PORT + - name: WELL_KNOWN_URLS + valueFrom: + configMapKeyRef: + name: interop-be-common-refactor + key: WELL_KNOWN_URLS + - name: READMODEL_DB_USERNAME + valueFrom: + secretKeyRef: + name: read-model + key: REFACTOR_USERNAME + - name: READMODEL_DB_PASSWORD + valueFrom: + secretKeyRef: + name: read-model + key: REFACTOR_USER_PASSWORD + - name: PRODUCER_ALLOWED_ORIGINS + valueFrom: + configMapKeyRef: + name: interop-be-authorization-process-refactor + key: PRODUCER_ALLOWED_ORIGINS diff --git a/packages/authorization-process/kubernetes/dev/service.yaml b/packages/authorization-process/kubernetes/dev/service.yaml new file mode 100644 index 0000000000..d5ab8957ad --- /dev/null +++ b/packages/authorization-process/kubernetes/dev/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: interop-be-authorization-process-refactor + namespace: dev-refactor +spec: + type: ClusterIP + ports: + - port: 3000 + name: http + targetPort: http + protocol: TCP + selector: + app: interop-be-authorization-process-refactor diff --git a/packages/authorization-process/kubernetes/dev/serviceAccount.yaml b/packages/authorization-process/kubernetes/dev/serviceAccount.yaml new file mode 100644 index 0000000000..d0b14317c0 --- /dev/null +++ b/packages/authorization-process/kubernetes/dev/serviceAccount.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: interop-be-authorization-process-refactor + namespace: dev-refactor + labels: + app.kubernetes.io/name: interop-be-authorization-process-refactor + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::505630707203:role/interop-be-authorization-process-refactor-dev diff --git a/packages/authorization-process/open-api/authorization-service-spec.yml b/packages/authorization-process/open-api/authorization-service-spec.yml new file mode 100644 index 0000000000..c65abb9d23 --- /dev/null +++ b/packages/authorization-process/open-api/authorization-service-spec.yml @@ -0,0 +1,1264 @@ +openapi: 3.0.3 + +info: + title: Security Process Micro Service + description: This service is the security supplier + version: "{{version}}" + contact: + name: API Support + url: http://www.example.com/support + email: support@example.com + termsOfService: "http://swagger.io/terms/" + x-api-id: an x-api-id + x-summary: an x-summary +servers: + - url: "/authorization-process/{{version}}" + description: This service is the security supplier +security: + - bearerAuth: [] +tags: + - name: client + description: Get security information + externalDocs: + description: Find out more + url: http://swagger.io + - name: user + description: Get security information + externalDocs: + description: Find out more + url: http://swagger.io + - name: health + description: Verify service status + externalDocs: + description: Find out more + url: http://swagger.io +paths: + /clientsConsumer: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - client + summary: Create a new consumer client + description: Create a new consumer client + operationId: createConsumerClient + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ClientSeed" + responses: + "200": + description: Client created + content: + application/json: + schema: + $ref: "#/components/schemas/Client" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + /clientsApi: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - client + summary: Create a new API client + description: Create a new API client + operationId: createApiClient + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ClientSeed" + responses: + "200": + description: Client created + content: + application/json: + schema: + $ref: "#/components/schemas/Client" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + /clientsWithKeys: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + get: + tags: + - client + summary: List clients with keys + description: List clients with keys + operationId: getClientsWithKeys + parameters: + - in: query + name: name + description: Filter for the client name + schema: + type: string + - in: query + name: userIds + description: comma separated sequence of user IDs + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - name: consumerId + in: query + description: ID of Consumer that MUST be related to the Client + required: true + schema: + type: string + format: uuid + - name: purposeId + in: query + description: ID of Purpose that MUST be related to the Client + schema: + type: string + format: uuid + - name: kind + in: query + description: type of Client to be retrieved + schema: + $ref: "#/components/schemas/ClientKind" + - in: query + name: offset + required: true + schema: + type: integer + format: int32 + minimum: 0 + - in: query + name: limit + required: true + schema: + type: integer + format: int32 + minimum: 1 + maximum: 50 + responses: + "200": + description: Clients found + content: + application/json: + schema: + $ref: "#/components/schemas/ClientsWithKeys" + "400": + description: Bad Request + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + /clients: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + get: + tags: + - client + summary: List clients + description: List clients + operationId: getClients + parameters: + - in: query + name: name + description: Filter for the client name + schema: + type: string + - in: query + name: userIds + description: comma separated sequence of user IDs + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - name: consumerId + in: query + description: ID of Consumer that MUST be related to the Client + required: true + schema: + type: string + format: uuid + - name: purposeId + in: query + description: ID of Purpose that MUST be related to the Client + schema: + type: string + format: uuid + - name: kind + in: query + description: type of Client to be retrieved + schema: + $ref: "#/components/schemas/ClientKind" + - in: query + name: offset + required: true + schema: + type: integer + format: int32 + minimum: 0 + - in: query + name: limit + required: true + schema: + type: integer + format: int32 + minimum: 1 + maximum: 50 + responses: + "200": + description: Clients found + content: + application/json: + schema: + $ref: "#/components/schemas/Clients" + "400": + description: Bad Request + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + /clients/{clientId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + get: + description: Retrieves a Client + tags: + - client + summary: Get a Client + operationId: getClient + parameters: + - name: clientId + in: path + description: The Client id + required: true + schema: + type: string + responses: + "200": + description: Client retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Client" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Client not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + delete: + description: Deletes a Client + tags: + - client + summary: Delete a Client + operationId: deleteClient + parameters: + - name: clientId + in: path + description: The Client id + required: true + schema: + type: string + responses: + "204": + description: Client deleted + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Client not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "/clients/{clientId}/users": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + get: + tags: + - client + summary: List client users + description: List client users + operationId: getClientUsers + parameters: + - name: clientId + in: path + description: ID of Client the users belong to + required: true + schema: + type: string + format: uuid + responses: + "200": + description: Request succeed + content: + application/json: + schema: + $ref: "#/components/schemas/Users" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Not Found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "/clients/{clientId}/users/{userId}": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: clientId + in: path + description: The Client id + required: true + schema: + type: string + format: uuid + - name: userId + in: path + description: The identifier of the user between the security user and the consumer + required: true + schema: + type: string + format: uuid + delete: + description: Removes a user from a Client + tags: + - client + summary: Remove an user from a Client + operationId: removeUser + responses: + "204": + description: User removed + "400": + description: Bad Request + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Client or User not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + post: + tags: + - client + summary: Add an user to a Client + operationId: addUser + responses: + "200": + description: User added + content: + application/json: + schema: + $ref: "#/components/schemas/Client" + "400": + description: Bad Request + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Missing Required Information + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + description: Add an user to a Client + "/clients/{clientId}/keys": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - client + summary: Create Keys for the specific clientId. + operationId: createKeys + responses: + "200": + description: Keys created + content: + application/json: + schema: + $ref: "#/components/schemas/Keys" + "400": + description: Bad Request + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Client id not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + requestBody: + required: true + description: an array of base64 encoded PEM keys. + content: + application/json: + schema: + $ref: "#/components/schemas/KeysSeed" + description: Creates one or more keys for the corresponding client. + parameters: + - name: clientId + in: path + required: true + description: ID of client that the added keys MUST belong to + schema: + type: string + format: uuid + get: + tags: + - client + summary: Returns a set of keys by client ID. + description: "Given a client identifier it returns its corresponding set of keys, if any" + operationId: getClientKeys + parameters: + - name: clientId + in: path + description: ID of the client to look up + required: true + schema: + type: string + format: uuid + - name: userIds + in: query + description: comma separated sequence of user IDs + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + responses: + "200": + description: returns the corresponding array of keys + content: + application/json: + schema: + $ref: "#/components/schemas/Keys" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Client id not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "/clients/{clientId}/keys/{keyId}": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + get: + tags: + - client + summary: Returns a key by client and key identifier (kid). + description: "Given a client and key identifiers it returns the corresponding key, if any" + operationId: getClientKeyById + parameters: + - name: clientId + in: path + description: ID of the client to look up + required: true + schema: + type: string + format: uuid + - name: keyId + in: path + description: the unique identifier of the key (kid) to lookup + required: true + schema: + type: string + responses: + "200": + description: returns the corresponding key + content: + application/json: + schema: + $ref: "#/components/schemas/Key" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Key not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + delete: + tags: + - client + summary: Deletes a key by client and key identifier (kid). + description: "Given a client and key identifiers it deletes the corresponding key, if any" + operationId: deleteClientKeyById + parameters: + - name: clientId + in: path + description: ID of the client holding the key + required: true + schema: + type: string + format: uuid + - name: keyId + in: path + description: the unique identifier of the key (kid) to delete + required: true + schema: + type: string + responses: + "204": + description: the corresponding key has been deleted. + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Key not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "/clients/{clientId}/users/{userId}/keys": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: clientId + in: path + description: ID of the client holding the key + required: true + schema: + type: string + format: uuid + - name: userId + in: path + required: true + description: ID of the User that the added keys MUST belong to + schema: + type: string + format: uuid + get: + tags: + - user + summary: Returns a set of keys by user ID and client ID. + description: "Given an user and a client it returns its corresponding set of keys, if any" + operationId: getClientUserKeys + responses: + "200": + description: returns the corresponding array of keys + content: + application/json: + schema: + $ref: "#/components/schemas/Keys" + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Client id not found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "/clients/{clientId}/purposes": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - client + summary: Adds a purpose to a client + description: Adds a purpose to a client + operationId: addClientPurpose + parameters: + - name: clientId + in: path + description: ID of Client + required: true + schema: + type: string + format: uuid + requestBody: + required: true + description: Details of the Purpose to add + content: + application/json: + schema: + $ref: "#/components/schemas/PurposeAdditionDetails" + responses: + "204": + description: Request succeed + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Not Found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "/clients/{clientId}/purposes/{purposeId}": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + delete: + tags: + - client + summary: Removes a purpose from a client + description: Removes a purpose from a client + operationId: removeClientPurpose + parameters: + - name: clientId + in: path + description: ID of Client + required: true + schema: + type: string + format: uuid + - name: purposeId + in: path + description: ID of Purpose + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Request succeed + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Not Found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + /clients/purposes/{purposeId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + delete: + tags: + - client + summary: Removes the purpose from all clients + description: Removes the purpose from all clients + operationId: removePurposeFromClients + parameters: + - name: purposeId + in: path + description: ID of Purpose + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Request succeed + "401": + description: Unauthorized + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Purpose Not Found + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" + /status: + get: + security: [] + tags: + - health + summary: Health status endpoint + description: Return ok + operationId: getStatus + responses: + "200": + description: successful operation + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" +components: + parameters: + CorrelationIdHeader: + in: header + name: X-Correlation-Id + required: true + schema: + type: string + schemas: + ClientSeed: + description: Client creation request body + type: object + properties: + name: + type: string + minLength: 5 + maxLength: 60 + description: + type: string + minLength: 10 + maxLength: 250 + members: + items: + type: string + format: uuid + required: + - name + - members + Client: + description: Models Client details + type: object + properties: + id: + type: string + format: uuid + name: + type: string + consumerId: + type: string + format: uuid + createdAt: + type: string + format: date-time + purposes: + type: array + items: + $ref: "#/components/schemas/ClientPurpose" + default: [] + description: + type: string + users: + type: array + items: + type: string + format: uuid + uniqueItems: true + kind: + $ref: "#/components/schemas/ClientKind" + required: + - id + - name + - consumerId + - purposes + - users + - kind + - createdAt + ClientPurpose: + type: object + description: Models Client purposes + properties: + states: + $ref: "#/components/schemas/ClientStatesChain" + required: + - states + ClientKind: + type: string + description: kind of client + enum: + - CONSUMER + - API + ClientWithKeys: + type: object + properties: + client: + $ref: "#/components/schemas/Client" + keys: + type: array + items: + $ref: "#/components/schemas/Key" + required: + - client + - keys + ClientsWithKeys: + type: object + properties: + results: + type: array + items: + $ref: "#/components/schemas/ClientWithKeys" + totalCount: + type: integer + format: int32 + required: + - results + - totalCount + Clients: + type: object + properties: + results: + type: array + items: + $ref: "#/components/schemas/Client" + totalCount: + type: integer + format: int32 + required: + - results + - totalCount + Users: + type: array + items: + type: string + format: uuid + Key: + description: "Models the PersistentKey" + type: object + properties: + userId: + type: string + format: uuid + description: "Represents the identifier of the user" + kid: + type: string + name: + type: string + encodedPem: + type: string + algorithm: + type: string + use: + $ref: "#/components/schemas/KeyUse" + createdAt: + type: string + format: date-time + required: + - userId + - kid + - name + - encodedPem + - algorithm + - use + - createdAt + Keys: + type: object + properties: + keys: + type: array + items: + $ref: "#/components/schemas/Key" + required: + - keys + OtherPrimeInfo: + title: OtherPrimeInfo + type: object + properties: + r: + type: string + d: + type: string + t: + type: string + required: + - r + - d + - t + KeysSeed: + type: array + items: + $ref: "#/components/schemas/KeySeed" + KeySeed: + description: "Models the seed for a public key to be persisted" + type: object + properties: + key: + type: string + description: "Base64 UTF-8 encoding of a public key in PEM format" + use: + $ref: "#/components/schemas/KeyUse" + alg: + type: string + description: "The algorithm type of the key." + name: + type: string + description: "Name given to the current key." + minLength: 5 + maxLength: 60 + required: + - key + - use + - alg + - name + KeyUse: + type: string + description: Represents the Use field of key + enum: + - SIG + - ENC + Organization: + description: Models an Organization + type: object + properties: + institutionId: + type: string + description: + type: string + required: + - institutionId + - description + Purpose: + type: object + properties: + purposeId: + type: string + format: uuid + title: + type: string + states: + $ref: "#/components/schemas/ClientStatesChain" + agreement: + $ref: "#/components/schemas/Agreement" + required: + - purposeId + - title + - states + - agreement + PurposeAdditionDetails: + type: object + properties: + purposeId: + type: string + format: uuid + required: + - purposeId + ClientStatesChain: + type: object + properties: + id: + type: string + format: uuid + eservice: + $ref: "#/components/schemas/ClientEServiceDetails" + agreement: + $ref: "#/components/schemas/ClientAgreementDetails" + purpose: + $ref: "#/components/schemas/ClientPurposeDetails" + required: + - id + - eservice + - agreement + - purpose + ClientEServiceDetails: + type: object + properties: + eserviceId: + type: string + format: uuid + descriptorId: + type: string + format: uuid + state: + $ref: "#/components/schemas/ClientComponentState" + audience: + type: array + items: + type: string + voucherLifespan: + type: integer + format: int32 + required: + - eserviceId + - descriptorId + - state + - audience + - voucherLifespan + ClientAgreementDetails: + type: object + properties: + eserviceId: + type: string + format: uuid + consumerId: + type: string + format: uuid + agreementId: + type: string + format: uuid + state: + $ref: "#/components/schemas/ClientComponentState" + required: + - eserviceId + - consumerId + - agreementId + - state + ClientPurposeDetails: + type: object + properties: + purposeId: + type: string + format: uuid + versionId: + type: string + format: uuid + state: + $ref: "#/components/schemas/ClientComponentState" + required: + - purposeId + - versionId + - state + ClientComponentState: + type: string + description: Represents the State of an object related to the purpose + enum: + - ACTIVE + - INACTIVE + Agreement: + type: object + properties: + id: + type: string + format: uuid + eservice: + $ref: "#/components/schemas/EService" + descriptor: + $ref: "#/components/schemas/EServiceDescriptor" + required: + - id + - eservice + - descriptor + EService: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + required: + - id + - name + EServiceDescriptor: + type: object + properties: + id: + type: string + format: uuid + version: + type: string + required: + - id + - version + Problem: + properties: + type: + description: URI reference of type definition + type: string + status: + description: The HTTP status code generated by the origin server for this occurrence of the problem. + example: 503 + exclusiveMaximum: true + format: int32 + maximum: 600 + minimum: 100 + type: integer + title: + description: A short, summary of the problem type. Written in english and readable + example: Service Unavailable + maxLength: 64 + pattern: "^[ -~]{0,64}$" + type: string + correlationId: + description: Unique identifier of the request + example: "53af4f2d-0c87-41ef-a645-b726a821852b" + maxLength: 64 + type: string + detail: + description: A human readable explanation of the problem. + example: Request took too long to complete. + maxLength: 4096 + pattern: "^.{0,1024}$" + type: string + errors: + type: array + minItems: 1 + items: + $ref: "#/components/schemas/ProblemError" + additionalProperties: false + required: + - type + - status + - title + - errors + ProblemError: + properties: + code: + description: Internal code of the error + example: 123-4567 + minLength: 8 + maxLength: 8 + pattern: "^[0-9]{3}-[0-9]{4}$" + type: string + detail: + description: A human readable explanation specific to this occurrence of the problem. + example: Parameter not valid + maxLength: 4096 + pattern: "^.{0,1024}$" + type: string + required: + - code + - detail + securitySchemes: + bearerAuth: + type: http + description: A bearer token in the format of a JWS and comformed to the specifications included in [RFC8725](https://tools.ietf.org/html/RFC8725). + scheme: bearer + bearerFormat: JWT diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json new file mode 100644 index 0000000000..4e45451416 --- /dev/null +++ b/packages/authorization-process/package.json @@ -0,0 +1,51 @@ +{ + "name": "pagopa-interop-authorization-process", + "version": "1.0.0", + "description": "PagoPA Interoperability service for authorization", + "main": "dist", + "type": "module", + "scripts": { + "test": "vitest", + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "node --watch --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json", + "generate-model": "mkdir -p ./src/model/generated && pnpm openapi-zod-client './open-api/authorization-service-spec.yml' -o './src/model/generated/api.ts'", + "clean-generated": "pnpm exec rm ./src/model/generated/api.ts" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@protobuf-ts/runtime": "2.9.4", + "@types/dotenv-flow": "3.3.3", + "@types/express": "4.17.21", + "@types/node": "20.3.1", + "@types/uuid": "9.0.8", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "ts-node": "10.9.2", + "typescript": "5.1.3", + "vitest": "1.5.2" + }, + "dependencies": { + "@zodios/core": "10.9.6", + "@zodios/express": "10.6.1", + "axios": "1.6.8", + "connection-string": "4.4.0", + "dotenv-flow": "3.3.0", + "express": "4.19.2", + "mongodb": "5.9.2", + "openapi-zod-client": "1.15.1", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "pg-promise": "11.5.0", + "ts-pattern": "5.0.6", + "uuid": "9.0.1", + "zod": "3.22.3" + } +} diff --git a/packages/authorization-process/src/app.ts b/packages/authorization-process/src/app.ts new file mode 100644 index 0000000000..7e0397dc4f --- /dev/null +++ b/packages/authorization-process/src/app.ts @@ -0,0 +1,22 @@ +import { + authenticationMiddleware, + contextMiddleware, + zodiosCtx, +} from "pagopa-interop-commons"; +import healthRouter from "./routers/HealthRouter.js"; +import authorizationRouter from "./routers/AuthorizationRouter.js"; + +const serviceName = "attribute-registry-process"; + +const app = zodiosCtx.app(); + +// Disable the "X-Powered-By: Express" HTTP header for security reasons. +// See https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#recommendation_16 +app.disable("x-powered-by"); + +app.use(contextMiddleware(serviceName)); +app.use(healthRouter); +app.use(authenticationMiddleware); +app.use(authorizationRouter(zodiosCtx)); + +export default app; diff --git a/packages/authorization-process/src/index.ts b/packages/authorization-process/src/index.ts new file mode 100644 index 0000000000..59c097f74f --- /dev/null +++ b/packages/authorization-process/src/index.ts @@ -0,0 +1,7 @@ +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "./utilities/config.js"; +import app from "./app.js"; + +app.listen(config.port, config.host, () => { + genericLogger.info(`listening on ${config.host}:${config.port}`); +}); diff --git a/packages/authorization-process/src/model/domain/apiConverter.ts b/packages/authorization-process/src/model/domain/apiConverter.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/authorization-process/src/model/domain/errors.ts b/packages/authorization-process/src/model/domain/errors.ts new file mode 100644 index 0000000000..c98ab058c2 --- /dev/null +++ b/packages/authorization-process/src/model/domain/errors.ts @@ -0,0 +1,9 @@ +import { makeApiProblemBuilder } from "pagopa-interop-models"; + +export const errorCodes = { + clientNotFound: "0001", +}; + +export type ErrorCodes = keyof typeof errorCodes; + +export const makeApiProblem = makeApiProblemBuilder(errorCodes); diff --git a/packages/authorization-process/src/model/domain/models.ts b/packages/authorization-process/src/model/domain/models.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/authorization-process/src/model/domain/toEvent.ts b/packages/authorization-process/src/model/domain/toEvent.ts new file mode 100644 index 0000000000..2f71f452d9 --- /dev/null +++ b/packages/authorization-process/src/model/domain/toEvent.ts @@ -0,0 +1 @@ +// this will contain the toCreateEvent{...} functions diff --git a/packages/authorization-process/src/routers/AuthorizationRouter.ts b/packages/authorization-process/src/routers/AuthorizationRouter.ts new file mode 100644 index 0000000000..f8921d95e8 --- /dev/null +++ b/packages/authorization-process/src/routers/AuthorizationRouter.ts @@ -0,0 +1,123 @@ +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { + ExpressContext, + userRoles, + ZodiosContext, + authorizationMiddleware, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { api } from "../model/generated/api.js"; + +/* +const readModelRepository = ReadModelRepository.init(config); +const readModelService = readModelServiceBuilder(readModelRepository); +const authorizationService = authorizationServiceBuilder( + initDB({ + username: config.eventStoreDbUsername, + password: config.eventStoreDbPassword, + host: config.eventStoreDbHost, + port: config.eventStoreDbPort, + database: config.eventStoreDbName, + schema: config.eventStoreDbSchema, + useSSL: config.eventStoreDbUseSSL, + }), + readModelService +); +*/ + +const authorizationRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const authorizationRouter = ctx.router(api.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + const { ADMIN_ROLE } = userRoles; + authorizationRouter + .post( + "/clientsConsumer", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .post( + "/clientsApi", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .get( + "/clientsWithKeys", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .get("/clients", authorizationMiddleware([ADMIN_ROLE]), async (_req, res) => + res.status(501).send() + ) + .get( + "/clients/:clientId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .delete( + "/clients/:clientId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .get( + "/clients/:clientId/users", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .delete( + "/clients/:clientId/users/:userId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .post( + "/clients/:clientId/users/:userId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .post( + "/clients/:clientId/keys", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .get( + "/clients/:clientId/keys", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .get( + "/clients/:clientId/keys/:keyId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .delete( + "/clients/:clientId/keys/:keyId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .get( + "/clients/:clientId/users/:userId/keys", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .post( + "/clients/:clientId/purposes", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .delete( + "/clients/:clientId/purposes/:purposeId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ) + .delete( + "/clients/purposes/:purposeId", + authorizationMiddleware([ADMIN_ROLE]), + async (_req, res) => res.status(501).send() + ); + + return authorizationRouter; +}; +export default authorizationRouter; diff --git a/packages/authorization-process/src/routers/HealthRouter.ts b/packages/authorization-process/src/routers/HealthRouter.ts new file mode 100644 index 0000000000..c8d635d5de --- /dev/null +++ b/packages/authorization-process/src/routers/HealthRouter.ts @@ -0,0 +1,8 @@ +import { zodiosRouter } from "@zodios/express"; +import { api } from "../model/generated/api.js"; + +const healthRouter = zodiosRouter(api.api); + +healthRouter.get("/status", async (_, res) => res.status(200).end()); + +export default healthRouter; diff --git a/packages/authorization-process/src/services/authorizationService.ts b/packages/authorization-process/src/services/authorizationService.ts new file mode 100644 index 0000000000..41446db1ca --- /dev/null +++ b/packages/authorization-process/src/services/authorizationService.ts @@ -0,0 +1,20 @@ +import { DB } from "pagopa-interop-commons"; +import { ReadModelService } from "./readModelService.js"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function authorizationServiceBuilder( + _dbInstance: DB, + _readModelService: ReadModelService +) { + // const repository = eventRepository(dbInstance, attributeEventToBinaryData); + + return { + sample(): string { + return "sample"; + }, + }; +} + +export type AuthorizationService = ReturnType< + typeof authorizationServiceBuilder +>; diff --git a/packages/authorization-process/src/services/readModelService.ts b/packages/authorization-process/src/services/readModelService.ts new file mode 100644 index 0000000000..3dcabb6b6b --- /dev/null +++ b/packages/authorization-process/src/services/readModelService.ts @@ -0,0 +1,16 @@ +import { ReadModelRepository } from "pagopa-interop-commons"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function readModelServiceBuilder( + _readModelRepository: ReadModelRepository +) { + // const { clients } = readModelRepository; + + return { + sample(): string { + return "sample"; + }, + }; +} + +export type ReadModelService = ReturnType; diff --git a/packages/authorization-process/src/utilities/config.ts b/packages/authorization-process/src/utilities/config.ts new file mode 100644 index 0000000000..2003398623 --- /dev/null +++ b/packages/authorization-process/src/utilities/config.ts @@ -0,0 +1,15 @@ +import { + CommonHTTPServiceConfig, + ReadModelDbConfig, + EventStoreConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const AuthorizationConfig = + CommonHTTPServiceConfig.and(ReadModelDbConfig).and(EventStoreConfig); + +export type AuthorizationConfig = z.infer; + +export const config: AuthorizationConfig = { + ...AuthorizationConfig.parse(process.env), +}; diff --git a/packages/authorization-process/src/utilities/errorMappers.ts b/packages/authorization-process/src/utilities/errorMappers.ts new file mode 100644 index 0000000000..3b00ed9d8d --- /dev/null +++ b/packages/authorization-process/src/utilities/errorMappers.ts @@ -0,0 +1 @@ +// this will contain the error mappers diff --git a/packages/authorization-process/test/.eslintrc.json b/packages/authorization-process/test/.eslintrc.json new file mode 100644 index 0000000000..6135a5ce08 --- /dev/null +++ b/packages/authorization-process/test/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["../../../.eslintrc.cjs"], + "rules": { + "functional/immutable-data": "off", + "sonarjs/no-identical-functions": "off" + } +} diff --git a/packages/authorization-process/test/authorizationService.integration.test.ts b/packages/authorization-process/test/authorizationService.integration.test.ts new file mode 100644 index 0000000000..9fe3565baa --- /dev/null +++ b/packages/authorization-process/test/authorizationService.integration.test.ts @@ -0,0 +1,9 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { describe, expect, it } from "vitest"; + +describe("sample test", () => { + it("sample", () => { + expect(1).toBe(1); + }); +}); diff --git a/packages/authorization-process/test/tsconfig.json b/packages/authorization-process/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/authorization-process/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/authorization-process/test/vitestGlobalSetup.ts b/packages/authorization-process/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/authorization-process/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/authorization-process/tsconfig.check.json b/packages/authorization-process/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/authorization-process/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/authorization-process/tsconfig.json b/packages/authorization-process/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/authorization-process/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/authorization-process/vitest.config.ts b/packages/authorization-process/vitest.config.ts new file mode 100644 index 0000000000..d1a4f22cc5 --- /dev/null +++ b/packages/authorization-process/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globalSetup: ["./test/vitestGlobalSetup.ts"], + testTimeout: 60000, + hookTimeout: 60000, + fileParallelism: false, + pool: "forks" + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae0240eb83..ad03d1f7f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -313,6 +313,85 @@ importers: specifier: 1.5.2 version: 1.5.2(@types/node@20.3.1) + packages/authorization-process: + dependencies: + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.6.8)(zod@3.22.3) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6)(express@4.19.2)(zod@3.22.3) + axios: + specifier: 1.6.8 + version: 1.6.8 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 3.3.0 + version: 3.3.0 + express: + specifier: 4.19.2 + version: 4.19.2 + mongodb: + specifier: 5.9.2 + version: 5.9.2 + openapi-zod-client: + specifier: 1.15.1 + version: 1.15.1 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + pg-promise: + specifier: 11.5.0 + version: 11.5.0 + ts-pattern: + specifier: 5.0.6 + version: 5.0.6 + uuid: + specifier: 9.0.1 + version: 9.0.1 + zod: + specifier: 3.22.3 + version: 3.22.3 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(typescript@5.1.3) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/dotenv-flow': + specifier: 3.3.3 + version: 3.3.3 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.3.1 + version: 20.3.1 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + ts-node: + specifier: 10.9.2 + version: 10.9.2(@types/node@20.3.1)(typescript@5.1.3) + typescript: + specifier: 5.1.3 + version: 5.1.3 + vitest: + specifier: 1.5.2 + version: 1.5.2(@types/node@20.3.1) + packages/authorization-updater: dependencies: '@protobuf-ts/runtime': @@ -2960,16 +3039,6 @@ packages: dev: true optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.44.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.44.0 - eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2990,23 +3059,6 @@ packages: engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true - /@eslint/eslintrc@2.1.0: - resolution: {integrity: sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - /@eslint/eslintrc@2.1.4: resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3024,11 +3076,6 @@ packages: - supports-color dev: true - /@eslint/js@8.44.0: - resolution: {integrity: sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /@eslint/js@8.57.0: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3045,17 +3092,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} dev: true - /@humanwhocodes/config-array@0.11.10: - resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -3072,10 +3108,6 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - dev: true - /@humanwhocodes/object-schema@2.0.3: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} dev: true @@ -3179,19 +3211,19 @@ packages: /@pagopa/eslint-config@3.0.0(typescript@5.1.3): resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} dependencies: - '@typescript-eslint/eslint-plugin': 5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.44.0)(typescript@5.1.3) - '@typescript-eslint/parser': 5.60.0(eslint@8.44.0)(typescript@5.1.3) - eslint: 8.44.0 - eslint-config-prettier: 8.8.0(eslint@8.44.0) + '@typescript-eslint/eslint-plugin': 5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.57.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.60.0(eslint@8.57.0)(typescript@5.1.3) + eslint: 8.57.0 + eslint-config-prettier: 8.8.0(eslint@8.57.0) eslint-plugin-extra-rules: 0.0.0-development - eslint-plugin-fp: 2.3.0(eslint@8.44.0) - eslint-plugin-functional: 4.4.1(eslint@8.44.0)(typescript@5.1.3) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.60.0)(eslint@8.44.0) - eslint-plugin-jsdoc: 39.9.1(eslint@8.44.0) - eslint-plugin-prefer-arrow: 1.2.3(eslint@8.44.0) - eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.44.0)(prettier@2.8.8) - eslint-plugin-react: 7.32.2(eslint@8.44.0) - eslint-plugin-sonarjs: 0.13.0(eslint@8.44.0) + eslint-plugin-fp: 2.3.0(eslint@8.57.0) + eslint-plugin-functional: 4.4.1(eslint@8.57.0)(typescript@5.1.3) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.60.0)(eslint@8.57.0) + eslint-plugin-jsdoc: 39.9.1(eslint@8.57.0) + eslint-plugin-prefer-arrow: 1.2.3(eslint@8.57.0) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.57.0)(prettier@2.8.8) + eslint-plugin-react: 7.32.2(eslint@8.57.0) + eslint-plugin-sonarjs: 0.13.0(eslint@8.57.0) prettier: 2.8.8 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -4424,7 +4456,7 @@ packages: '@types/webidl-conversions': 7.0.0 dev: false - /@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.44.0)(typescript@5.1.3): + /@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.57.0)(typescript@5.1.3): resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -4436,12 +4468,12 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.60.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.60.0(eslint@8.57.0)(typescript@5.1.3) '@typescript-eslint/scope-manager': 5.60.0 - '@typescript-eslint/type-utils': 5.60.0(eslint@8.44.0)(typescript@5.1.3) - '@typescript-eslint/utils': 5.60.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/type-utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) + '@typescript-eslint/utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) debug: 4.3.4 - eslint: 8.44.0 + eslint: 8.57.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 @@ -4452,7 +4484,7 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@5.60.0(eslint@8.44.0)(typescript@5.1.3): + /@typescript-eslint/parser@5.60.0(eslint@8.57.0)(typescript@5.1.3): resolution: {integrity: sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -4466,7 +4498,7 @@ packages: '@typescript-eslint/types': 5.60.0 '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) debug: 4.3.4 - eslint: 8.44.0 + eslint: 8.57.0 typescript: 5.1.3 transitivePeerDependencies: - supports-color @@ -4480,7 +4512,7 @@ packages: '@typescript-eslint/visitor-keys': 5.60.0 dev: true - /@typescript-eslint/type-utils@5.60.0(eslint@8.44.0)(typescript@5.1.3): + /@typescript-eslint/type-utils@5.60.0(eslint@8.57.0)(typescript@5.1.3): resolution: {integrity: sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -4491,9 +4523,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) - '@typescript-eslint/utils': 5.60.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) debug: 4.3.4 - eslint: 8.44.0 + eslint: 8.57.0 tsutils: 3.21.0(typescript@5.1.3) typescript: 5.1.3 transitivePeerDependencies: @@ -4526,19 +4558,19 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.60.0(eslint@8.44.0)(typescript@5.1.3): + /@typescript-eslint/utils@5.60.0(eslint@8.57.0)(typescript@5.1.3): resolution: {integrity: sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.12 '@types/semver': 7.5.0 '@typescript-eslint/scope-manager': 5.60.0 '@typescript-eslint/types': 5.60.0 '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) - eslint: 8.44.0 + eslint: 8.57.0 eslint-scope: 5.1.1 semver: 7.6.0 transitivePeerDependencies: @@ -4651,11 +4683,6 @@ packages: acorn: 8.11.3 dev: true - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - dev: true - /acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -4673,12 +4700,6 @@ packages: hasBin: true dev: true - /acorn@8.9.0: - resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - /ajv-draft-04@1.0.0(ajv@8.12.0): resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -5599,13 +5620,13 @@ packages: lodash.zip: 4.2.0 dev: true - /eslint-config-prettier@8.8.0(eslint@8.44.0): + /eslint-config-prettier@8.8.0(eslint@8.57.0): resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: - eslint: 8.44.0 + eslint: 8.57.0 dev: true /eslint-import-resolver-node@0.3.7: @@ -5618,7 +5639,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.60.0)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.60.0)(eslint-import-resolver-node@0.3.7)(eslint@8.57.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -5639,9 +5660,9 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.60.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.60.0(eslint@8.57.0)(typescript@5.1.3) debug: 3.2.7 - eslint: 8.44.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.7 transitivePeerDependencies: - supports-color @@ -5656,20 +5677,20 @@ packages: quote: 0.4.0 dev: true - /eslint-plugin-fp@2.3.0(eslint@8.44.0): + /eslint-plugin-fp@2.3.0(eslint@8.57.0): resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} engines: {node: '>=4.0.0'} peerDependencies: eslint: '>=3' dependencies: create-eslint-index: 1.0.0 - eslint: 8.44.0 + eslint: 8.57.0 eslint-ast-utils: 1.1.0 lodash: 4.17.21 req-all: 0.1.0 dev: true - /eslint-plugin-functional@4.4.1(eslint@8.44.0)(typescript@5.1.3): + /eslint-plugin-functional@4.4.1(eslint@8.57.0)(typescript@5.1.3): resolution: {integrity: sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -5682,17 +5703,17 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.60.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) deepmerge-ts: 4.3.0 escape-string-regexp: 4.0.0 - eslint: 8.44.0 + eslint: 8.57.0 semver: 7.6.0 typescript: 5.1.3 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.60.0)(eslint@8.44.0): + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.60.0)(eslint@8.57.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} peerDependencies: @@ -5702,15 +5723,15 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.60.0(eslint@8.44.0)(typescript@5.1.3) + '@typescript-eslint/parser': 5.60.0(eslint@8.57.0)(typescript@5.1.3) array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.44.0 + eslint: 8.57.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.60.0)(eslint-import-resolver-node@0.3.7)(eslint@8.44.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.60.0)(eslint-import-resolver-node@0.3.7)(eslint@8.57.0) has: 1.0.3 is-core-module: 2.12.1 is-glob: 4.0.3 @@ -5725,7 +5746,7 @@ packages: - supports-color dev: true - /eslint-plugin-jsdoc@39.9.1(eslint@8.44.0): + /eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} peerDependencies: @@ -5735,7 +5756,7 @@ packages: comment-parser: 1.3.1 debug: 4.3.4 escape-string-regexp: 4.0.0 - eslint: 8.44.0 + eslint: 8.57.0 esquery: 1.5.0 semver: 7.6.0 spdx-expression-parse: 3.0.1 @@ -5743,15 +5764,15 @@ packages: - supports-color dev: true - /eslint-plugin-prefer-arrow@1.2.3(eslint@8.44.0): + /eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} peerDependencies: eslint: '>=2.0.0' dependencies: - eslint: 8.44.0 + eslint: 8.57.0 dev: true - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0)(eslint@8.44.0)(prettier@2.8.8): + /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.8.0)(eslint@8.57.0)(prettier@2.8.8): resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} engines: {node: '>=12.0.0'} peerDependencies: @@ -5762,13 +5783,13 @@ packages: eslint-config-prettier: optional: true dependencies: - eslint: 8.44.0 - eslint-config-prettier: 8.8.0(eslint@8.44.0) + eslint: 8.57.0 + eslint-config-prettier: 8.8.0(eslint@8.57.0) prettier: 2.8.8 prettier-linter-helpers: 1.0.0 dev: true - /eslint-plugin-react@7.32.2(eslint@8.44.0): + /eslint-plugin-react@7.32.2(eslint@8.57.0): resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} engines: {node: '>=4'} peerDependencies: @@ -5778,7 +5799,7 @@ packages: array.prototype.flatmap: 1.3.1 array.prototype.tosorted: 1.1.1 doctrine: 2.1.0 - eslint: 8.44.0 + eslint: 8.57.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.3 minimatch: 3.1.2 @@ -5792,13 +5813,13 @@ packages: string.prototype.matchall: 4.0.8 dev: true - /eslint-plugin-sonarjs@0.13.0(eslint@8.44.0): + /eslint-plugin-sonarjs@0.13.0(eslint@8.57.0): resolution: {integrity: sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==} engines: {node: '>=12'} peerDependencies: eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.44.0 + eslint: 8.57.0 dev: true /eslint-scope@5.1.1: @@ -5809,14 +5830,6 @@ packages: estraverse: 4.3.0 dev: true - /eslint-scope@7.2.0: - resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5825,64 +5838,11 @@ packages: estraverse: 5.3.0 dev: true - /eslint-visitor-keys@3.4.1: - resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /eslint@8.44.0: - resolution: {integrity: sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.44.0) - '@eslint-community/regexpp': 4.5.1 - '@eslint/eslintrc': 2.1.0 - '@eslint/js': 8.44.0 - '@humanwhocodes/config-array': 0.11.10 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.0 - eslint-visitor-keys: 3.4.1 - espree: 9.6.0 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - graphemer: 1.4.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.3 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - /eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -5939,15 +5899,6 @@ packages: acorn-jsx: 2.0.1 dev: true - /espree@9.6.0: - resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.11.3 - acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 - dev: true - /espree@9.6.1: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7044,7 +6995,7 @@ packages: resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==} dependencies: acorn: 8.11.3 - pathe: 1.1.1 + pathe: 1.1.2 pkg-types: 1.0.3 ufo: 1.1.2 dev: true @@ -7372,10 +7323,6 @@ packages: engines: {node: '>=8'} dev: true - /pathe@1.1.1: - resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} - dev: true - /pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} dev: true @@ -7468,7 +7415,7 @@ packages: dependencies: jsonc-parser: 3.2.0 mlly: 1.4.0 - pathe: 1.1.1 + pathe: 1.1.2 dev: true /postcss@8.4.38: @@ -8249,8 +8196,8 @@ packages: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 20.3.1 - acorn: 8.9.0 - acorn-walk: 8.2.0 + acorn: 8.11.3 + acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 From 7bbc114b05ae8ca310a6d6fdd5e3dd08fe6cca94 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 17 May 2024 10:36:29 +0200 Subject: [PATCH 517/537] Add comment --- packages/authorization-process/src/model/domain/models.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/authorization-process/src/model/domain/models.ts b/packages/authorization-process/src/model/domain/models.ts index e69de29bb2..40791a6e77 100644 --- a/packages/authorization-process/src/model/domain/models.ts +++ b/packages/authorization-process/src/model/domain/models.ts @@ -0,0 +1 @@ +// this will contain api types From 59291a22f632e13bdbc30f8891d3809403f27640 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 17 May 2024 10:37:45 +0200 Subject: [PATCH 518/537] Fix --- packages/authorization-process/src/app.ts | 2 +- .../authorization-process/src/services/authorizationService.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/authorization-process/src/app.ts b/packages/authorization-process/src/app.ts index 7e0397dc4f..202a282335 100644 --- a/packages/authorization-process/src/app.ts +++ b/packages/authorization-process/src/app.ts @@ -6,7 +6,7 @@ import { import healthRouter from "./routers/HealthRouter.js"; import authorizationRouter from "./routers/AuthorizationRouter.js"; -const serviceName = "attribute-registry-process"; +const serviceName = "authorization-process"; const app = zodiosCtx.app(); diff --git a/packages/authorization-process/src/services/authorizationService.ts b/packages/authorization-process/src/services/authorizationService.ts index 41446db1ca..0f9dcbe890 100644 --- a/packages/authorization-process/src/services/authorizationService.ts +++ b/packages/authorization-process/src/services/authorizationService.ts @@ -6,7 +6,7 @@ export function authorizationServiceBuilder( _dbInstance: DB, _readModelService: ReadModelService ) { - // const repository = eventRepository(dbInstance, attributeEventToBinaryData); + // const repository = eventRepository(dbInstance, authorizationEventToBinaryData); return { sample(): string { From 5ebef221048bf7d03db2efeebbc85e75a93c45a2 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 17 May 2024 10:54:47 +0200 Subject: [PATCH 519/537] Remove unused var --- .../authorization-process/kubernetes/dev/deployment.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/authorization-process/kubernetes/dev/deployment.yaml b/packages/authorization-process/kubernetes/dev/deployment.yaml index afac93ebdb..5adbb56e19 100644 --- a/packages/authorization-process/kubernetes/dev/deployment.yaml +++ b/packages/authorization-process/kubernetes/dev/deployment.yaml @@ -115,8 +115,3 @@ spec: secretKeyRef: name: read-model key: REFACTOR_USER_PASSWORD - - name: PRODUCER_ALLOWED_ORIGINS - valueFrom: - configMapKeyRef: - name: interop-be-authorization-process-refactor - key: PRODUCER_ALLOWED_ORIGINS From 0ed4c990636edc62aac0f9ffe0a32df0149dd7cf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 20 May 2024 11:23:37 +0200 Subject: [PATCH 520/537] Fix pnpm-lock file --- pnpm-lock.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78c5ef31f4..c1c1d20616 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -550,8 +550,8 @@ importers: specifier: 1.7.0 version: 1.7.0 connection-string: - specifier: 4.3.6 - version: 4.3.6 + specifier: 4.4.0 + version: 4.4.0 date-fns: specifier: 3.3.1 version: 3.3.1 @@ -5167,11 +5167,6 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true - /connection-string@4.3.6: - resolution: {integrity: sha512-dwaq4BMeiIIWry/oQxjstPB7Xp0K1nGjaY8p6PHQB+J9ZJuIvNp7ux3Noupq0hMd/dqEHXkfdmmGFOKTbGKWmw==} - engines: {node: '>=12', npm: '>=6'} - dev: false - /connection-string@4.4.0: resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} engines: {node: '>=14'} @@ -5597,6 +5592,7 @@ packages: /eslint-config-prettier@8.8.0(eslint@8.57.0): resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: @@ -8101,6 +8097,7 @@ packages: /ts-node@10.9.2(@types/node@20.3.1)(typescript@5.4.5): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true peerDependencies: '@swc/core': '>=1.2.50' '@swc/wasm': '>=1.2.50' @@ -8328,6 +8325,7 @@ packages: /update-browserslist-db@1.0.11(browserslist@4.21.9): resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: @@ -8383,6 +8381,7 @@ packages: /vite@5.2.9(@types/node@20.3.1): resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: '@types/node': ^18.0.0 || >=20.0.0 less: '*' @@ -8418,6 +8417,7 @@ packages: /vitest@1.6.0(@types/node@20.3.1): resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 From cc689e9834c79b638e636352adaf3e63a3cf4c01 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 20 May 2024 12:40:15 +0200 Subject: [PATCH 521/537] Update api spec --- .../open-api/authorization-service-spec.yml | 109 +----------------- 1 file changed, 2 insertions(+), 107 deletions(-) diff --git a/packages/authorization-process/open-api/authorization-service-spec.yml b/packages/authorization-process/open-api/authorization-service-spec.yml index c65abb9d23..492e51b7d7 100644 --- a/packages/authorization-process/open-api/authorization-service-spec.yml +++ b/packages/authorization-process/open-api/authorization-service-spec.yml @@ -883,7 +883,8 @@ components: purposes: type: array items: - $ref: "#/components/schemas/ClientPurpose" + type: string + format: uuid default: [] description: type: string @@ -903,14 +904,6 @@ components: - users - kind - createdAt - ClientPurpose: - type: object - description: Models Client purposes - properties: - states: - $ref: "#/components/schemas/ClientStatesChain" - required: - - states ClientKind: type: string description: kind of client @@ -1055,23 +1048,6 @@ components: required: - institutionId - description - Purpose: - type: object - properties: - purposeId: - type: string - format: uuid - title: - type: string - states: - $ref: "#/components/schemas/ClientStatesChain" - agreement: - $ref: "#/components/schemas/Agreement" - required: - - purposeId - - title - - states - - agreement PurposeAdditionDetails: type: object properties: @@ -1080,87 +1056,6 @@ components: format: uuid required: - purposeId - ClientStatesChain: - type: object - properties: - id: - type: string - format: uuid - eservice: - $ref: "#/components/schemas/ClientEServiceDetails" - agreement: - $ref: "#/components/schemas/ClientAgreementDetails" - purpose: - $ref: "#/components/schemas/ClientPurposeDetails" - required: - - id - - eservice - - agreement - - purpose - ClientEServiceDetails: - type: object - properties: - eserviceId: - type: string - format: uuid - descriptorId: - type: string - format: uuid - state: - $ref: "#/components/schemas/ClientComponentState" - audience: - type: array - items: - type: string - voucherLifespan: - type: integer - format: int32 - required: - - eserviceId - - descriptorId - - state - - audience - - voucherLifespan - ClientAgreementDetails: - type: object - properties: - eserviceId: - type: string - format: uuid - consumerId: - type: string - format: uuid - agreementId: - type: string - format: uuid - state: - $ref: "#/components/schemas/ClientComponentState" - required: - - eserviceId - - consumerId - - agreementId - - state - ClientPurposeDetails: - type: object - properties: - purposeId: - type: string - format: uuid - versionId: - type: string - format: uuid - state: - $ref: "#/components/schemas/ClientComponentState" - required: - - purposeId - - versionId - - state - ClientComponentState: - type: string - description: Represents the State of an object related to the purpose - enum: - - ACTIVE - - INACTIVE Agreement: type: object properties: From ea30de3e600fbf68da6019b36f6bbcca38a29e46 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 20 May 2024 14:34:30 +0200 Subject: [PATCH 522/537] Remove empty files --- packages/authorization-process/src/model/domain/models.ts | 1 - packages/authorization-process/src/model/domain/toEvent.ts | 1 - 2 files changed, 2 deletions(-) delete mode 100644 packages/authorization-process/src/model/domain/models.ts delete mode 100644 packages/authorization-process/src/model/domain/toEvent.ts diff --git a/packages/authorization-process/src/model/domain/models.ts b/packages/authorization-process/src/model/domain/models.ts deleted file mode 100644 index 40791a6e77..0000000000 --- a/packages/authorization-process/src/model/domain/models.ts +++ /dev/null @@ -1 +0,0 @@ -// this will contain api types diff --git a/packages/authorization-process/src/model/domain/toEvent.ts b/packages/authorization-process/src/model/domain/toEvent.ts deleted file mode 100644 index 2f71f452d9..0000000000 --- a/packages/authorization-process/src/model/domain/toEvent.ts +++ /dev/null @@ -1 +0,0 @@ -// this will contain the toCreateEvent{...} functions From 05021e86ce1773a7f528c6ed595ca6202bf64532 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 20 May 2024 14:35:26 +0200 Subject: [PATCH 523/537] Remove empty file --- packages/authorization-process/src/utilities/errorMappers.ts | 1 - 1 file changed, 1 deletion(-) delete mode 100644 packages/authorization-process/src/utilities/errorMappers.ts diff --git a/packages/authorization-process/src/utilities/errorMappers.ts b/packages/authorization-process/src/utilities/errorMappers.ts deleted file mode 100644 index 3b00ed9d8d..0000000000 --- a/packages/authorization-process/src/utilities/errorMappers.ts +++ /dev/null @@ -1 +0,0 @@ -// this will contain the error mappers From 312f4389266a68229d76af84983069839180f31f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 20 May 2024 15:21:53 +0200 Subject: [PATCH 524/537] Fix --- packages/authorization-process/package.json | 15 +- pnpm-lock.yaml | 481 +------------------- 2 files changed, 25 insertions(+), 471 deletions(-) diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json index 4e45451416..e3c6d3fc53 100644 --- a/packages/authorization-process/package.json +++ b/packages/authorization-process/package.json @@ -29,23 +29,22 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", - "typescript": "5.1.3", - "vitest": "1.5.2" + "typescript": "5.4.5", + "vitest": "1.6.0" }, "dependencies": { "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", - "axios": "1.6.8", + "axios": "1.7.0", "connection-string": "4.4.0", "dotenv-flow": "3.3.0", "express": "4.19.2", "mongodb": "5.9.2", - "openapi-zod-client": "1.15.1", + "openapi-zod-client": "1.18.1", "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", - "pg-promise": "11.5.0", - "ts-pattern": "5.0.6", - "uuid": "9.0.1", - "zod": "3.22.3" + "pg-promise": "11.6.0", + "ts-pattern": "5.1.1", + "zod": "3.23.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06ce5e37d6..241c738212 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -317,13 +317,13 @@ importers: dependencies: '@zodios/core': specifier: 10.9.6 - version: 10.9.6(axios@1.6.8)(zod@3.22.3) + version: 10.9.6(axios@1.7.0)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.19.2)(zod@3.22.3) + version: 10.6.1(@zodios/core@10.9.6)(express@4.19.2)(zod@3.23.8) axios: - specifier: 1.6.8 - version: 1.6.8 + specifier: 1.7.0 + version: 1.7.0 connection-string: specifier: 4.4.0 version: 4.4.0 @@ -337,8 +337,8 @@ importers: specifier: 5.9.2 version: 5.9.2 openapi-zod-client: - specifier: 1.15.1 - version: 1.15.1 + specifier: 1.18.1 + version: 1.18.1 pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -346,21 +346,18 @@ importers: specifier: workspace:* version: link:../models pg-promise: - specifier: 11.5.0 - version: 11.5.0 + specifier: 11.6.0 + version: 11.6.0 ts-pattern: - specifier: 5.0.6 - version: 5.0.6 - uuid: - specifier: 9.0.1 - version: 9.0.1 + specifier: 5.1.1 + version: 5.1.1 zod: - specifier: 3.22.3 - version: 3.22.3 + specifier: 3.23.8 + version: 3.23.8 devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.1.3) + version: 3.0.0(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -384,13 +381,13 @@ importers: version: 2.8.8 ts-node: specifier: 10.9.2 - version: 10.9.2(@types/node@20.3.1)(typescript@5.1.3) + version: 10.9.2(@types/node@20.3.1)(typescript@5.4.5) typescript: - specifier: 5.1.3 - version: 5.1.3 + specifier: 5.4.5 + version: 5.4.5 vitest: - specifier: 1.5.2 - version: 1.5.2(@types/node@20.3.1) + specifier: 1.6.0 + version: 1.6.0(@types/node@20.3.1) packages/authorization-updater: dependencies: @@ -3282,31 +3279,6 @@ packages: fastq: 1.15.0 dev: true - /@pagopa/eslint-config@3.0.0(typescript@5.1.3): - resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - dependencies: - '@typescript-eslint/eslint-plugin': 5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.57.0)(typescript@5.1.3) - '@typescript-eslint/parser': 5.60.0(eslint@8.57.0)(typescript@5.1.3) - eslint: 8.57.0 - eslint-config-prettier: 8.8.0(eslint@8.57.0) - eslint-plugin-extra-rules: 0.0.0-development - eslint-plugin-fp: 2.3.0(eslint@8.57.0) - eslint-plugin-functional: 4.4.1(eslint@8.57.0)(typescript@5.1.3) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.60.0)(eslint@8.57.0) - eslint-plugin-jsdoc: 39.9.1(eslint@8.57.0) - eslint-plugin-prefer-arrow: 1.2.3(eslint@8.57.0) - eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.8.0)(eslint@8.57.0)(prettier@2.8.8) - eslint-plugin-react: 7.32.2(eslint@8.57.0) - eslint-plugin-sonarjs: 0.13.0(eslint@8.57.0) - prettier: 2.8.8 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - tsutils - - typescript - dev: true - /@pagopa/eslint-config@3.0.0(typescript@5.4.5): resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} dependencies: @@ -4555,34 +4527,6 @@ packages: '@types/webidl-conversions': 7.0.0 dev: false - /@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.57.0)(typescript@5.1.3): - resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.60.0(eslint@8.57.0)(typescript@5.1.3) - '@typescript-eslint/scope-manager': 5.60.0 - '@typescript-eslint/type-utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) - '@typescript-eslint/utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) - debug: 4.3.4 - eslint: 8.57.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - natural-compare-lite: 1.4.0 - semver: 7.6.0 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/eslint-plugin@5.60.0(@typescript-eslint/parser@5.60.0)(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4611,26 +4555,6 @@ packages: - supports-color dev: true - /@typescript-eslint/parser@5.60.0(eslint@8.57.0)(typescript@5.1.3): - resolution: {integrity: sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.60.0 - '@typescript-eslint/types': 5.60.0 - '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) - debug: 4.3.4 - eslint: 8.57.0 - typescript: 5.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/parser@5.60.0(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4659,26 +4583,6 @@ packages: '@typescript-eslint/visitor-keys': 5.60.0 dev: true - /@typescript-eslint/type-utils@5.60.0(eslint@8.57.0)(typescript@5.1.3): - resolution: {integrity: sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) - '@typescript-eslint/utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) - debug: 4.3.4 - eslint: 8.57.0 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/type-utils@5.60.0(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4704,27 +4608,6 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.60.0(typescript@5.1.3): - resolution: {integrity: sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.60.0 - '@typescript-eslint/visitor-keys': 5.60.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.6.0 - tsutils: 3.21.0(typescript@5.1.3) - typescript: 5.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/typescript-estree@5.60.0(typescript@5.4.5): resolution: {integrity: sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4746,26 +4629,6 @@ packages: - supports-color dev: true - /@typescript-eslint/utils@5.60.0(eslint@8.57.0)(typescript@5.1.3): - resolution: {integrity: sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@types/json-schema': 7.0.12 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.60.0 - '@typescript-eslint/types': 5.60.0 - '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) - eslint: 8.57.0 - eslint-scope: 5.1.1 - semver: 7.6.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - /@typescript-eslint/utils@5.60.0(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4798,14 +4661,6 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitest/expect@1.5.2: - resolution: {integrity: sha512-rf7MTD1WCoDlN3FfYJ9Llfp0PbdtOMZ3FIF0AVkDnKbp3oiMW1c8AmvRZBcqbAhDUAvF52e9zx4WQM1r3oraVA==} - dependencies: - '@vitest/spy': 1.5.2 - '@vitest/utils': 1.5.2 - chai: 4.4.1 - dev: true - /@vitest/expect@1.6.0: resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} dependencies: @@ -4814,14 +4669,6 @@ packages: chai: 4.4.1 dev: true - /@vitest/runner@1.5.2: - resolution: {integrity: sha512-7IJ7sJhMZrqx7HIEpv3WrMYcq8ZNz9L6alo81Y6f8hV5mIE6yVZsFoivLZmr0D777klm1ReqonE9LyChdcmw6g==} - dependencies: - '@vitest/utils': 1.5.2 - p-limit: 5.0.0 - pathe: 1.1.2 - dev: true - /@vitest/runner@1.6.0: resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} dependencies: @@ -4830,14 +4677,6 @@ packages: pathe: 1.1.2 dev: true - /@vitest/snapshot@1.5.2: - resolution: {integrity: sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==} - dependencies: - magic-string: 0.30.10 - pathe: 1.1.2 - pretty-format: 29.7.0 - dev: true - /@vitest/snapshot@1.6.0: resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} dependencies: @@ -4846,27 +4685,12 @@ packages: pretty-format: 29.7.0 dev: true - /@vitest/spy@1.5.2: - resolution: {integrity: sha512-xCcPvI8JpCtgikT9nLpHPL1/81AYqZy1GCy4+MCHBE7xi8jgsYkULpW5hrx5PGLgOQjUpb6fd15lqcriJ40tfQ==} - dependencies: - tinyspy: 2.2.1 - dev: true - /@vitest/spy@1.6.0: resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} dependencies: tinyspy: 2.2.1 dev: true - /@vitest/utils@1.5.2: - resolution: {integrity: sha512-sWOmyofuXLJ85VvXNsroZur7mOJGiQeM0JN3/0D1uU8U9bGFM69X1iqHaRXl6R8BwaLY6yPCogP257zxTzkUdA==} - dependencies: - diff-sequences: 29.6.3 - estree-walker: 3.0.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - dev: true - /@vitest/utils@1.6.0: resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} dependencies: @@ -4876,16 +4700,6 @@ packages: pretty-format: 29.7.0 dev: true - /@zodios/core@10.9.6(axios@1.6.8)(zod@3.22.3): - resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} - peerDependencies: - axios: ^0.x || ^1.0.0 - zod: ^3.x - dependencies: - axios: 1.6.8 - zod: 3.22.3 - dev: false - /@zodios/core@10.9.6(axios@1.7.0)(zod@3.23.8): resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} peerDependencies: @@ -4895,18 +4709,6 @@ packages: axios: 1.7.0 zod: 3.23.8 - /@zodios/express@10.6.1(@zodios/core@10.9.6)(express@4.19.2)(zod@3.22.3): - resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} - peerDependencies: - '@zodios/core': '>=10.4.4 <11.0.0' - express: 4.x - zod: ^3.x - dependencies: - '@zodios/core': 10.9.6(axios@1.6.8)(zod@3.22.3) - express: 4.19.2 - zod: 3.22.3 - dev: false - /@zodios/express@10.6.1(@zodios/core@10.9.6)(express@4.19.2)(zod@3.23.8): resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} peerDependencies: @@ -5152,16 +4954,6 @@ packages: engines: {node: '>= 0.4'} dev: true - /axios@1.6.8: - resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} - dependencies: - follow-redirects: 1.15.6 - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - dev: false - /axios@1.7.0: resolution: {integrity: sha512-IiB0wQeKyPRdsFVhBgIo31FbzOyf2M6wYl7/NVutFwFBRMiAbjNiydJIHKeLmPugF4kJLfA1uWZ82Is2QzqqFA==} dependencies: @@ -5294,11 +5086,6 @@ packages: /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - /buffer-writer@2.0.0: - resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} - engines: {node: '>=4'} - dev: false - /buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: @@ -5964,29 +5751,6 @@ packages: req-all: 0.1.0 dev: true - /eslint-plugin-functional@4.4.1(eslint@8.57.0)(typescript@5.1.3): - resolution: {integrity: sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^8.0.0 - tsutils: ^3.0.0 - typescript: ^3.4.1 || ^4.0.0 - peerDependenciesMeta: - tsutils: - optional: true - typescript: - optional: true - dependencies: - '@typescript-eslint/utils': 5.60.0(eslint@8.57.0)(typescript@5.1.3) - deepmerge-ts: 4.3.0 - escape-string-regexp: 4.0.0 - eslint: 8.57.0 - semver: 7.6.0 - typescript: 5.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /eslint-plugin-functional@4.4.1(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-YhSfHS52Si62Sn126g9wGx+XnWMoWhwEt6ctVXfcJj+xMUiggjOqUVMca7fuLNzX8jYiNBIeU1Y0teHGePZ3NA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -7503,31 +7267,6 @@ packages: /openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - /openapi-zod-client@1.15.1: - resolution: {integrity: sha512-2WWy3O28KGHE1+HKY49RUs1XFXCeOIwXY+6NsmoGlBn5N2LrZUfPMN/B6EW60U5Xt2O535TYMT+5SYfRqTvDBQ==} - hasBin: true - dependencies: - '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) - '@liuli-util/fs-extra': 0.1.0 - '@zodios/core': 10.9.6(axios@1.7.0)(zod@3.23.8) - axios: 1.7.0 - cac: 6.7.14 - handlebars: 4.7.7 - openapi-types: 12.1.3 - openapi3-ts: 3.1.0 - pastable: 2.2.1 - prettier: 2.8.8 - tanu: 0.1.13 - ts-pattern: 5.1.1 - whence: 2.0.0 - zod: 3.23.8 - transitivePeerDependencies: - - debug - - react - - supports-color - - xstate - dev: false - /openapi-zod-client@1.18.1: resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} hasBin: true @@ -7590,10 +7329,6 @@ packages: p-limit: 3.1.0 dev: true - /packet-reader@1.0.0: - resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} - dev: false - /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -7670,10 +7405,6 @@ packages: requiresBuild: true optional: true - /pg-connection-string@2.6.0: - resolution: {integrity: sha512-x14ibktcwlHKoHxx9X3uTVW9zIGR41ZB6QNhHb21OPNdCCO3NaRnpJuwKIQSR4u+Yqjx4HCvy7Hh7VSy1U4dGg==} - dev: false - /pg-connection-string@2.6.4: resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} @@ -7685,14 +7416,6 @@ packages: resolution: {integrity: sha512-NoSsPqXxbkD8RIe+peQCqiea4QzXgosdTKY8p7PsbbGsh2F8TifDj/vJxfuR8qJwNYrijdSs7uf0tAe6WOyCsQ==} engines: {node: '>=12.0.0'} - /pg-pool@3.6.0(pg@8.11.0): - resolution: {integrity: sha512-clFRf2ksqd+F497kWFyM21tMjeikn60oGDmqMT8UBrynEwVEX/5R5xd2sdvdo1cZCFlguORNpVuqxIj+aK4cfQ==} - peerDependencies: - pg: '>=8.0' - dependencies: - pg: 8.11.0 - dev: false - /pg-pool@3.6.2(pg@8.11.5): resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} peerDependencies: @@ -7700,18 +7423,6 @@ packages: dependencies: pg: 8.11.5 - /pg-promise@11.5.0: - resolution: {integrity: sha512-ZfhntV6Yoc3S0hQWOlEodk5fEmF9ADxKl0vNvBnZgzvLt73uY29wVaNBz2AZK2J0gVmm/zhO51RXPtI4MgKkSQ==} - engines: {node: '>=14.0'} - dependencies: - assert-options: 0.8.1 - pg: 8.11.0 - pg-minify: 1.6.3 - spex: 3.3.0 - transitivePeerDependencies: - - pg-native - dev: false - /pg-promise@11.6.0: resolution: {integrity: sha512-NDRPMfkv3ia89suWlJ4iGvP6X5YFrLJ2+9AIVISeBFFZ29Eb4FNXX9JaVb1p1OrpQkE2yT7igmXPL7UYQhk+6A==} engines: {node: '>=14.0'} @@ -7723,10 +7434,6 @@ packages: transitivePeerDependencies: - pg-native - /pg-protocol@1.6.0: - resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} - dev: false - /pg-protocol@1.6.1: resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} @@ -7740,26 +7447,6 @@ packages: postgres-date: 1.0.7 postgres-interval: 1.2.0 - /pg@8.11.0: - resolution: {integrity: sha512-meLUVPn2TWgJyLmy7el3fQQVwft4gU5NGyvV0XbD41iU9Jbg8lCH4zexhIkihDzVHJStlt6r088G6/fWeNjhXA==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true - dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.6.0 - pg-pool: 3.6.0(pg@8.11.0) - pg-protocol: 1.6.0 - pg-types: 2.2.0 - pgpass: 1.0.5 - optionalDependencies: - pg-cloudflare: 1.1.1 - dev: false - /pg@8.11.5: resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} engines: {node: '>= 8.0.0'} @@ -8534,37 +8221,6 @@ packages: resolution: {integrity: sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==} dev: false - /ts-node@10.9.2(@types/node@20.3.1)(typescript@5.1.3): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.3.1 - acorn: 8.11.3 - acorn-walk: 8.3.2 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.1.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - /ts-node@10.9.2(@types/node@20.3.1)(typescript@5.4.5): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -8596,10 +8252,6 @@ packages: yn: 3.1.1 dev: true - /ts-pattern@5.0.6: - resolution: {integrity: sha512-Y+jOjihlFriWzcBjncPCf2/am+Hgz7LtsWs77pWg5vQQKLQj07oNrJryo/wK2G0ndNaoVn2ownFMeoeAuReu3Q==} - dev: false - /ts-pattern@5.1.1: resolution: {integrity: sha512-i+owkHr5RYdQxj8olUgRrqpiWH9x27PuWVfXwDmJ/n/CoF/SAa7WW1i2oUpPDMQpJ4U+bGRUcZkVq7i1m3zFCg==} @@ -8634,16 +8286,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - /tsutils@3.21.0(typescript@5.1.3): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 5.1.3 - dev: true - /tsutils@3.21.0(typescript@5.4.5): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -8766,12 +8408,6 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - /typescript@5.1.3: - resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==} - engines: {node: '>=14.17'} - hasBin: true - dev: true - /typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} @@ -8859,27 +8495,6 @@ packages: engines: {node: '>= 0.8'} dev: false - /vite-node@1.5.2(@types/node@20.3.1): - resolution: {integrity: sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - dependencies: - cac: 6.7.14 - debug: 4.3.4 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.9(@types/node@20.3.1) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - /vite-node@1.6.0(@types/node@20.3.1): resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -8937,62 +8552,6 @@ packages: fsevents: 2.3.3 dev: true - /vitest@1.5.2(@types/node@20.3.1): - resolution: {integrity: sha512-l9gwIkq16ug3xY7BxHwcBQovLZG75zZL0PlsiYQbf76Rz6QGs54416UWMtC0jXeihvHvcHrf2ROEjkQRVpoZYw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.5.2 - '@vitest/ui': 1.5.2 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - dependencies: - '@types/node': 20.3.1 - '@vitest/expect': 1.5.2 - '@vitest/runner': 1.5.2 - '@vitest/snapshot': 1.5.2 - '@vitest/spy': 1.5.2 - '@vitest/utils': 1.5.2 - acorn-walk: 8.3.2 - chai: 4.4.1 - debug: 4.3.4 - execa: 8.0.1 - local-pkg: 0.5.0 - magic-string: 0.30.10 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 2.1.0 - tinybench: 2.7.0 - tinypool: 0.8.4 - vite: 5.2.9(@types/node@20.3.1) - vite-node: 1.5.2(@types/node@20.3.1) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - dev: true - /vitest@1.6.0(@types/node@20.3.1): resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9203,9 +8762,5 @@ packages: zod: 3.23.8 dev: false - /zod@3.22.3: - resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} - dev: false - /zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} From f9e490675e4462258504d9b3410fd46d8928773b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 20 May 2024 17:57:29 +0200 Subject: [PATCH 525/537] Fix image --- packages/authorization-process/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/authorization-process/Dockerfile b/packages/authorization-process/Dockerfile index 7c3f358bf3..115aa0a832 100644 --- a/packages/authorization-process/Dockerfile +++ b/packages/authorization-process/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18.20.2-slim@sha256:f2e7a19c91d98b854c226c04c4a1bf7d9d5fac28f320777efe5e01aa2e70c474 as build +FROM node:18.20.2-slim@sha256:2697eb70c760c3720ad9811871d5ab19c4ad2f2e0a3735785e531927ab99bd39 as build RUN corepack enable @@ -31,7 +31,7 @@ RUN pnpm build && \ packages/authorization-process/dist && \ find /out -exec touch -h --date=@0 {} \; -FROM node:18.20.2-slim@sha256:f2e7a19c91d98b854c226c04c4a1bf7d9d5fac28f320777efe5e01aa2e70c474 as final +FROM node:18.20.2-slim@sha256:2697eb70c760c3720ad9811871d5ab19c4ad2f2e0a3735785e531927ab99bd39 as final COPY --from=build /out /app From 2c4ea8afbad7f74df06870c3959466b6c3c374f3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 21 May 2024 11:55:01 +0200 Subject: [PATCH 526/537] Fix api spec --- .../open-api/authorization-service-spec.yml | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/authorization-process/open-api/authorization-service-spec.yml b/packages/authorization-process/open-api/authorization-service-spec.yml index 492e51b7d7..7aefddbc79 100644 --- a/packages/authorization-process/open-api/authorization-service-spec.yml +++ b/packages/authorization-process/open-api/authorization-service-spec.yml @@ -58,7 +58,7 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" /clientsApi: @@ -86,7 +86,7 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" /clientsWithKeys: @@ -157,19 +157,19 @@ paths: "400": description: Bad Request content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" /clients: @@ -240,19 +240,19 @@ paths: "400": description: Bad Request content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" /clients/{clientId}: @@ -281,19 +281,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Client not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" delete: @@ -315,19 +315,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Client not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "/clients/{clientId}/users": @@ -357,19 +357,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Not Found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "/clients/{clientId}/users/{userId}": @@ -401,25 +401,25 @@ paths: "400": description: Bad Request content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Client or User not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" post: @@ -437,25 +437,25 @@ paths: "400": description: Bad Request content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Missing Required Information content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" description: Add an user to a Client @@ -477,25 +477,25 @@ paths: "400": description: Bad Request content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Client id not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" requestBody: @@ -548,19 +548,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Client id not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "/clients/{clientId}/keys/{keyId}": @@ -596,19 +596,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Key not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" delete: @@ -637,19 +637,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Key not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "/clients/{clientId}/users/{userId}/keys": @@ -685,13 +685,13 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Client id not found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "/clients/{clientId}/purposes": @@ -724,19 +724,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Not Found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "/clients/{clientId}/purposes/{purposeId}": @@ -769,19 +769,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Not Found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" /clients/purposes/{purposeId}: @@ -807,19 +807,19 @@ paths: "401": description: Unauthorized content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "403": description: Forbidden content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" "404": description: Purpose Not Found content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" /status: @@ -834,7 +834,7 @@ paths: "200": description: successful operation content: - application/problem+json: + application/json: schema: $ref: "#/components/schemas/Problem" components: From c23f52eb79dde4469327a0e7be93d6965f8605a5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 22 May 2024 15:10:39 +0200 Subject: [PATCH 527/537] Add sql table --- docker/event-store-init.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docker/event-store-init.sql b/docker/event-store-init.sql index 0f90db6776..edbc16c04c 100644 --- a/docker/event-store-init.sql +++ b/docker/event-store-init.sql @@ -92,3 +92,22 @@ create table purpose.events ( PRIMARY KEY (sequence_num), UNIQUE (stream_id, version) ); + +create schema authorization; +create table authorization.events ( + sequence_num bigserial NOT NULL, + + stream_id uuid NOT NULL, + version bigint NOT NULL, + + correlation_id text, + + type text NOT NULL, + event_version int NOT NULL, + data bytea NOT NULL, + + log_date timestamptz NOT NULL DEFAULT now(), + + PRIMARY KEY (sequence_num), + UNIQUE (stream_id, version) +); From e9eb3837d4aa8b9e9bc4d58815700b2fbfdc8051 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 22 May 2024 15:23:40 +0200 Subject: [PATCH 528/537] Add placeholder name for authorization table --- docker/event-store-init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/event-store-init.sql b/docker/event-store-init.sql index edbc16c04c..e07ceebd85 100644 --- a/docker/event-store-init.sql +++ b/docker/event-store-init.sql @@ -93,8 +93,8 @@ create table purpose.events ( UNIQUE (stream_id, version) ); -create schema authorization; -create table authorization.events ( +create schema authorization_temp; +create table authorization_temp.events ( sequence_num bigserial NOT NULL, stream_id uuid NOT NULL, From 6edec50488cc2bed9d32506fecf2cbb24387c7ed Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 22 May 2024 15:42:26 +0200 Subject: [PATCH 529/537] Rename schema --- docker/event-store-init.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/event-store-init.sql b/docker/event-store-init.sql index e07ceebd85..b19dc2f19e 100644 --- a/docker/event-store-init.sql +++ b/docker/event-store-init.sql @@ -93,8 +93,8 @@ create table purpose.events ( UNIQUE (stream_id, version) ); -create schema authorization_temp; -create table authorization_temp.events ( +create schema "authorization"; +create table "authorization".events ( sequence_num bigserial NOT NULL, stream_id uuid NOT NULL, From 0b9e6cfac4102b7f43640c99449d72bf8bff7369 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 23 May 2024 12:12:29 +0200 Subject: [PATCH 530/537] Fix spacing --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7d74f1abc0..a0f7703fe6 100644 --- a/package.json +++ b/package.json @@ -35,8 +35,8 @@ "@tsconfig/node-lts": "18.12.5", "turbo": "1.13.3" }, - "config": { + "config": { "protocVersion": "26.1" - }, + }, "packageManager": "pnpm@8.15.8" } From 74eb05a753e69d8a002a2d117ef903ae0d5c3412 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 23 May 2024 12:12:33 +0200 Subject: [PATCH 531/537] Add script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a0f7703fe6..2f6825c77d 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "start:notifier-seeder": "turbo start --filter pagopa-interop-notifier-seeder", "start:purpose": "turbo start --filter pagopa-interop-purpose-process", "start:purpose-readmodel-writer": "turbo start --filter pagopa-interop-purpose-readmodel-writer", + "start:authorization": "turbo start --filter pagopa-interop-authorization-process", "test": "turbo test", "build": "turbo build", "check": "turbo check", From 07b5347bb45a60c29c24d8e30f96dfb6320c9c46 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 24 May 2024 16:11:24 +0200 Subject: [PATCH 532/537] Add latest changes in start script --- packages/authorization-process/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json index e3c6d3fc53..1decb8edf7 100644 --- a/packages/authorization-process/package.json +++ b/packages/authorization-process/package.json @@ -10,7 +10,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --watch --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", + "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", "generate-model": "mkdir -p ./src/model/generated && pnpm openapi-zod-client './open-api/authorization-service-spec.yml' -o './src/model/generated/api.ts'", From fddaf2ae6297460821904953757e870a2eb5ae39 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 30 May 2024 08:55:47 +0200 Subject: [PATCH 533/537] Fix import --- packages/catalog-process/src/services/catalogService.ts | 1 - packages/catalog-process/test/catalogService.integration.test.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index b15a809ae8..c1a14bf349 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -7,7 +7,6 @@ import { WithLogger, AppContext, eventRepository, - formatDateAndTime, hasPermission, riskAnalysisValidatedFormToNewRiskAnalysis, riskAnalysisValidatedFormToNewRiskAnalysisForm, diff --git a/packages/catalog-process/test/catalogService.integration.test.ts b/packages/catalog-process/test/catalogService.integration.test.ts index 8eeb4a92fb..6c556a9497 100644 --- a/packages/catalog-process/test/catalogService.integration.test.ts +++ b/packages/catalog-process/test/catalogService.integration.test.ts @@ -23,7 +23,6 @@ import { ReadModelRepository, TenantCollection, fileManagerDeleteError, - formatDateAndTime, genericLogger, initDB, initFileManager, From d4982c130c09cf8c17439fe8d27d9be283b8f1fb Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 30 May 2024 08:56:52 +0200 Subject: [PATCH 534/537] Renaming --- packages/authorization-process/kubernetes/dev/configmap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/authorization-process/kubernetes/dev/configmap.yaml b/packages/authorization-process/kubernetes/dev/configmap.yaml index d7b34c6589..71dfb6170b 100644 --- a/packages/authorization-process/kubernetes/dev/configmap.yaml +++ b/packages/authorization-process/kubernetes/dev/configmap.yaml @@ -5,4 +5,4 @@ metadata: namespace: dev-refactor data: EVENTSTORE_DB_USE_SSL: "true" - EVENTSTORE_DB_SCHEMA: "authorization" + EVENTSTORE_DB_SCHEMA: "dev-refactor_authz" From 91dcb7a42c9552cadfc27177d9aca9a0fa6700ef Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 31 May 2024 15:46:39 +0200 Subject: [PATCH 535/537] Update pnpm-lock file --- pnpm-lock.yaml | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c0971435c..4eb3a6ad2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4546,6 +4546,13 @@ packages: '@types/webidl-conversions': 7.0.0 dev: false + /@types/whatwg-url@8.2.2: + resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} + dependencies: + '@types/node': 20.12.13 + '@types/webidl-conversions': 7.0.0 + dev: false + /@types/yauzl@2.10.3: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true @@ -5143,6 +5150,11 @@ packages: node-releases: 2.0.12 update-browserslist-db: 1.0.11(browserslist@4.21.9) + /bson@5.5.1: + resolution: {integrity: sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==} + engines: {node: '>=14.20.1'} + dev: false + /bson@6.7.0: resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==} engines: {node: '>=16.20.1'} @@ -7360,6 +7372,13 @@ packages: ufo: 1.5.3 dev: true + /mongodb-connection-string-url@2.6.0: + resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} + dependencies: + '@types/whatwg-url': 8.2.2 + whatwg-url: 11.0.0 + dev: false + /mongodb-connection-string-url@3.0.1: resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} dependencies: @@ -7367,6 +7386,34 @@ packages: whatwg-url: 13.0.0 dev: false + /mongodb@5.9.2: + resolution: {integrity: sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==} + engines: {node: '>=14.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.0.0 + kerberos: ^1.0.0 || ^2.0.0 + mongodb-client-encryption: '>=2.3.0 <3' + snappy: ^7.2.2 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + dependencies: + bson: 5.5.1 + mongodb-connection-string-url: 2.6.0 + socks: 2.7.1 + optionalDependencies: + '@mongodb-js/saslprep': 1.1.7 + dev: false + /mongodb@6.7.0: resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} engines: {node: '>=16.20.1'} @@ -8664,6 +8711,13 @@ packages: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true + /tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.0 + dev: false + /tr46@4.1.1: resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} engines: {node: '>=14'} @@ -9303,6 +9357,14 @@ packages: engines: {node: '>=12'} dev: false + /whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: false + /whatwg-url@13.0.0: resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} engines: {node: '>=16'} From 2a77ffc0a4dbf4f6c9625d8989284a9693c1fac5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 11 Jun 2024 14:34:17 +0200 Subject: [PATCH 536/537] Update pnpm-lock file --- pnpm-lock.yaml | 421 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 420 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 128bfe0649..ad5cebe75a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -368,6 +368,82 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.2) + packages/authorization-process: + dependencies: + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.0)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6)(express@4.19.2)(zod@3.23.8) + axios: + specifier: 1.7.0 + version: 1.7.0 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 3.3.0 + version: 3.3.0 + express: + specifier: 4.19.2 + version: 4.19.2 + mongodb: + specifier: 5.9.2 + version: 5.9.2 + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + pg-promise: + specifier: 11.6.0 + version: 11.6.0 + ts-pattern: + specifier: 5.1.1 + version: 5.1.1 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/dotenv-flow': + specifier: 3.3.3 + version: 3.3.3 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.3.1 + version: 20.3.1 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + ts-node: + specifier: 10.9.2 + version: 10.9.2(@types/node@20.3.1)(typescript@5.4.5) + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.3.1) + packages/authorization-updater: dependencies: '@protobuf-ts/runtime': @@ -797,7 +873,7 @@ importers: version: 9.0.1 vitest: specifier: 1.6.0 - version: 1.6.0(@types/node@20.14.2) + version: 1.6.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -4894,6 +4970,10 @@ packages: '@types/node': 20.14.2 dev: true + /@types/dotenv-flow@3.3.3: + resolution: {integrity: sha512-aJjBsKw4bfGjvaRwrxBtEOfYZxCAq+LiFTpZ4DGTEK2b9eLVt/IAClapSxMfgV4Mi/2bIBKKjoTCO0lOh4ACLg==} + dev: true + /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true @@ -4957,6 +5037,10 @@ packages: dependencies: undici-types: 5.26.5 + /@types/node@20.3.1: + resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==} + dev: true + /@types/nodemailer@6.4.15: resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} dependencies: @@ -5015,6 +5099,13 @@ packages: '@types/webidl-conversions': 7.0.0 dev: false + /@types/whatwg-url@8.2.2: + resolution: {integrity: sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==} + dependencies: + '@types/node': 20.14.2 + '@types/webidl-conversions': 7.0.0 + dev: false + /@types/yauzl@2.10.3: resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} requiresBuild: true @@ -5195,6 +5286,16 @@ packages: pretty-format: 29.7.0 dev: true + /@zodios/core@10.9.6(axios@1.7.0)(zod@3.23.8): + resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} + peerDependencies: + axios: ^0.x || ^1.0.0 + zod: ^3.x + dependencies: + axios: 1.7.0 + zod: 3.23.8 + dev: false + /@zodios/core@10.9.6(axios@1.7.2)(zod@3.23.8): resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} peerDependencies: @@ -5484,6 +5585,16 @@ packages: - aws-crt dev: false + /axios@1.7.0: + resolution: {integrity: sha512-IiB0wQeKyPRdsFVhBgIo31FbzOyf2M6wYl7/NVutFwFBRMiAbjNiydJIHKeLmPugF4kJLfA1uWZ82Is2QzqqFA==} + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + /axios@1.7.2: resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} dependencies: @@ -5601,6 +5712,11 @@ packages: node-releases: 2.0.12 update-browserslist-db: 1.0.11(browserslist@4.21.9) + /bson@5.5.1: + resolution: {integrity: sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==} + engines: {node: '>=14.20.1'} + dev: false + /bson@6.7.0: resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==} engines: {node: '>=16.20.1'} @@ -6074,6 +6190,13 @@ packages: esutils: 2.0.3 dev: true + /dotenv-flow@3.3.0: + resolution: {integrity: sha512-GLSvRqDZ1TGhloS6ZCZ5chdqqv/3XMqZxAnX9rliJiHn6uyJLguKeu+3M2kcagBkoVCnLWYfbR4rfFe1xSU39A==} + engines: {node: '>= 8.0.0'} + dependencies: + dotenv: 8.6.0 + dev: false + /dotenv-flow@4.1.0: resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} engines: {node: '>= 12.0.0'} @@ -6084,6 +6207,11 @@ packages: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} + /dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + dev: false + /drange@1.1.1: resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} engines: {node: '>=4'} @@ -7746,6 +7874,13 @@ packages: ufo: 1.5.3 dev: true + /mongodb-connection-string-url@2.6.0: + resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==} + dependencies: + '@types/whatwg-url': 8.2.2 + whatwg-url: 11.0.0 + dev: false + /mongodb-connection-string-url@3.0.1: resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} dependencies: @@ -7753,6 +7888,34 @@ packages: whatwg-url: 13.0.0 dev: false + /mongodb@5.9.2: + resolution: {integrity: sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==} + engines: {node: '>=14.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.0.0 + kerberos: ^1.0.0 || ^2.0.0 + mongodb-client-encryption: '>=2.3.0 <3' + snappy: ^7.2.2 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + dependencies: + bson: 5.5.1 + mongodb-connection-string-url: 2.6.0 + socks: 2.7.1 + optionalDependencies: + '@mongodb-js/saslprep': 1.1.7 + dev: false + /mongodb@6.7.0: resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} engines: {node: '>=16.20.1'} @@ -8144,6 +8307,11 @@ packages: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} + /pg-minify@1.6.3: + resolution: {integrity: sha512-NoSsPqXxbkD8RIe+peQCqiea4QzXgosdTKY8p7PsbbGsh2F8TifDj/vJxfuR8qJwNYrijdSs7uf0tAe6WOyCsQ==} + engines: {node: '>=12.0.0'} + dev: false + /pg-minify@1.6.4: resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} engines: {node: '>=14.0.0'} @@ -8155,6 +8323,18 @@ packages: dependencies: pg: 8.11.5 + /pg-promise@11.6.0: + resolution: {integrity: sha512-NDRPMfkv3ia89suWlJ4iGvP6X5YFrLJ2+9AIVISeBFFZ29Eb4FNXX9JaVb1p1OrpQkE2yT7igmXPL7UYQhk+6A==} + engines: {node: '>=14.0'} + dependencies: + assert-options: 0.8.1 + pg: 8.11.5 + pg-minify: 1.6.3 + spex: 3.3.0 + transitivePeerDependencies: + - pg-native + dev: false + /pg-promise@11.8.0: resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} engines: {node: '>=14.0'} @@ -9019,6 +9199,13 @@ packages: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: true + /tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + dependencies: + punycode: 2.3.0 + dev: false + /tr46@4.1.1: resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} engines: {node: '>=14'} @@ -9061,6 +9248,41 @@ packages: yn: 3.1.1 dev: true + /ts-node@10.9.2(@types/node@20.3.1)(typescript@5.4.5): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.3.1 + acorn: 8.11.3 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.4.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ts-pattern@5.1.1: + resolution: {integrity: sha512-i+owkHr5RYdQxj8olUgRrqpiWH9x27PuWVfXwDmJ/n/CoF/SAa7WW1i2oUpPDMQpJ4U+bGRUcZkVq7i1m3zFCg==} + dev: false + /ts-pattern@5.1.2: resolution: {integrity: sha512-u+ElKUIWnqisjpRBhv6Y89yNq7Pmz6xL0v7pTSckrVZ0+5Vf32oh/3jmxWl80rAOGcnbBa7fCyeqNdP4yXzWWg==} @@ -9305,6 +9527,27 @@ packages: engines: {node: '>= 0.8'} dev: false + /vite-node@1.6.0: + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.2.9(@types/node@20.3.1) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite-node@1.6.0(@types/node@20.14.2): resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9326,6 +9569,27 @@ packages: - terser dev: true + /vite-node@1.6.0(@types/node@20.3.1): + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.2 + picocolors: 1.0.0 + vite: 5.2.9(@types/node@20.3.1) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite@5.2.9(@types/node@20.14.2): resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9362,6 +9626,97 @@ packages: fsevents: 2.3.3 dev: true + /vite@5.2.9(@types/node@20.3.1): + resolution: {integrity: sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.3.1 + esbuild: 0.20.2 + postcss: 8.4.38 + rollup: 4.14.3 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@1.6.0: + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@vitest/expect': 1.6.0 + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + acorn-walk: 8.3.2 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.10 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + tinybench: 2.7.0 + tinypool: 0.8.4 + vite: 5.2.9(@types/node@20.3.1) + vite-node: 1.6.0 + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vitest@1.6.0(@types/node@20.14.2): resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9418,6 +9773,62 @@ packages: - terser dev: true + /vitest@1.6.0(@types/node@20.3.1): + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.3.1 + '@vitest/expect': 1.6.0 + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 + acorn-walk: 8.3.2 + chai: 4.4.1 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.10 + pathe: 1.1.2 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + tinybench: 2.7.0 + tinypool: 0.8.4 + vite: 5.2.9(@types/node@20.3.1) + vite-node: 1.6.0(@types/node@20.3.1) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: true @@ -9427,6 +9838,14 @@ packages: engines: {node: '>=12'} dev: false + /whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + dev: false + /whatwg-url@13.0.0: resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} engines: {node: '>=16'} From b34f50f40fa600d7373a2c1e05612ce18e513b10 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 13 Jun 2024 12:07:53 +0200 Subject: [PATCH 537/537] Remove unused schemas in api spec --- .../open-api/authorization-service-spec.yml | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/packages/authorization-process/open-api/authorization-service-spec.yml b/packages/authorization-process/open-api/authorization-service-spec.yml index 7aefddbc79..3cf9df4aa8 100644 --- a/packages/authorization-process/open-api/authorization-service-spec.yml +++ b/packages/authorization-process/open-api/authorization-service-spec.yml @@ -1037,17 +1037,6 @@ components: enum: - SIG - ENC - Organization: - description: Models an Organization - type: object - properties: - institutionId: - type: string - description: - type: string - required: - - institutionId - - description PurposeAdditionDetails: type: object properties: @@ -1056,42 +1045,6 @@ components: format: uuid required: - purposeId - Agreement: - type: object - properties: - id: - type: string - format: uuid - eservice: - $ref: "#/components/schemas/EService" - descriptor: - $ref: "#/components/schemas/EServiceDescriptor" - required: - - id - - eservice - - descriptor - EService: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - required: - - id - - name - EServiceDescriptor: - type: object - properties: - id: - type: string - format: uuid - version: - type: string - required: - - id - - version Problem: properties: type: