From 8a787bf19bcff38501dddeef95ad9f8683b5976c Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 29 Oct 2024 13:01:09 +0100 Subject: [PATCH 001/126] IMN-810 - Aligning local dev AWS region with dev env (eu-south-1) (#1128) --- collections/bff/catalog/Import Eservice.bru | 2 +- collections/bff/catalog/Import Eservices.bru | 2 +- docker/docker-compose.yml | 4 ++-- packages/agreement-email-sender/.env | 2 +- packages/agreement-platformstate-writer/.env | 2 +- packages/agreement-process/aws.config.local | 2 +- packages/agreement-readmodel-writer/.env | 2 +- .../anac-certified-attributes-importer/aws.config.local | 2 +- packages/attribute-registry-readmodel-writer/.env | 2 +- packages/authorization-updater/.env | 2 +- packages/authorization-updater/aws.config.local | 2 +- packages/backend-for-frontend/aws.config.local | 2 +- packages/catalog-platformstate-writer/test/utils.test.ts | 2 +- packages/catalog-process/aws.config.local | 2 +- packages/catalog-readmodel-writer/.env | 2 +- packages/client-readmodel-writer/.env | 2 +- packages/commons-test/aws.config.local | 2 +- packages/commons-test/src/containerTestUtils.ts | 2 +- packages/commons-test/test/SES.test.ts | 2 +- packages/compute-agreements-consumer/.env | 2 +- packages/compute-agreements-consumer/aws.config.local | 2 +- packages/datalake-data-export/aws.config.local | 2 +- packages/dtd-catalog-exporter/.env | 2 +- packages/dtd-catalog-exporter/aws.config.local | 2 +- packages/eservice-descriptors-archiver/.env | 2 +- packages/eservice-descriptors-archiver/aws.config.local | 2 +- .../ivass-certified-attributes-importer/aws.config.local | 2 +- packages/kafka-iam-auth/README.md | 7 ++++--- packages/key-readmodel-writer/.env | 2 +- packages/notifier-seeder/.env | 2 +- packages/notifier-seeder/aws.config.local | 2 +- packages/one-trust-notices/aws.config.local | 2 +- packages/producer-key-events-writer/.env | 2 +- packages/producer-key-readmodel-writer/.env | 2 +- packages/producer-keychain-readmodel-writer/.env | 2 +- packages/purpose-process/aws.config.local | 2 +- packages/purpose-readmodel-writer/.env | 2 +- packages/selfcare-onboarding-consumer/.env | 2 +- packages/selfcare-onboarding-consumer/aws.config.local | 2 +- packages/tenant-readmodel-writer/.env | 2 +- 40 files changed, 44 insertions(+), 43 deletions(-) diff --git a/collections/bff/catalog/Import Eservice.bru b/collections/bff/catalog/Import Eservice.bru index 04d22b7d55..03b143fa97 100644 --- a/collections/bff/catalog/Import Eservice.bru +++ b/collections/bff/catalog/Import Eservice.bru @@ -21,6 +21,6 @@ headers { body:json { { "filename": "054d02f9-7beb-4443-994a-062c4157672e_1635b970-eb99-4c12-8a51-aac884dfd648.zip", - "url": "http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/054d02f9-7beb-4443-994a-062c4157672e_1635b970-eb99-4c12-8a51-aac884dfd648.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T132035Z&X-Amz-Expires=5000&X-Amz-Signature=b24b9928969cd58351e7c5d6c25e509c7b6f1436fc5835ef54c0331972b94c40&X-Amz-SignedHeaders=host&x-id=PutObject" + "url": "http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/054d02f9-7beb-4443-994a-062c4157672e_1635b970-eb99-4c12-8a51-aac884dfd648.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-south-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T132035Z&X-Amz-Expires=5000&X-Amz-Signature=b24b9928969cd58351e7c5d6c25e509c7b6f1436fc5835ef54c0331972b94c40&X-Amz-SignedHeaders=host&x-id=PutObject" } } diff --git a/collections/bff/catalog/Import Eservices.bru b/collections/bff/catalog/Import Eservices.bru index c113d42f95..a455a08d6a 100644 --- a/collections/bff/catalog/Import Eservices.bru +++ b/collections/bff/catalog/Import Eservices.bru @@ -18,5 +18,5 @@ headers { body:multipart-form { filename: f07b1c49-38fc-445e-a103-c2c156ae85ca_3bb5154d-eebc-42fd-9ea9-32b5be48e434.zip - url: http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/f07b1c49-38fc-445e-a103-c2c156ae85ca_3bb5154d-eebc-42fd-9ea9-32b5be48e434.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T140847Z&X-Amz-Expires=5000&X-Amz-Signature=d57f527006fce0a819c381821c688aa9d211a38f250f8bc450ff661ca06923d2&X-Amz-SignedHeaders=host&x-id=PutObject + url: http://localhost:9000/interop-application-import-export-local/local/eservices-import/69e2865e-65ab-4e48-a638-2037a9ee2ee7/f07b1c49-38fc-445e-a103-c2c156ae85ca_3bb5154d-eebc-42fd-9ea9-32b5be48e434.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=testawskey%2F20241001%2Feu-south-1%2Fs3%2Faws4_request&X-Amz-Date=20241001T140847Z&X-Amz-Expires=5000&X-Amz-Signature=d57f527006fce0a819c381821c688aa9d211a38f250f8bc450ff661ca06923d2&X-Amz-SignedHeaders=host&x-id=PutObject } diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2e73299cec..8a84f69746 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -163,7 +163,7 @@ services: environment: MINIO_ROOT_USER: testawskey # use this as AWS S3 access key ID MINIO_ROOT_PASSWORD: testawssecret # use this as AWS S3 secret access key - MINIO_SITE_REGION: eu-central-1 + MINIO_SITE_REGION: eu-south-1 volumes: - ./minio-data:/data restart: always @@ -208,7 +208,7 @@ services: - 4566:4566 environment: PORT: 4566 - KMS_REGION: eu-central-1 + KMS_REGION: eu-south-1 volumes: - ./local-kms-seed/seed.yaml:/init/seed.yaml diff --git a/packages/agreement-email-sender/.env b/packages/agreement-email-sender/.env index 73d796d577..cc67c376d8 100644 --- a/packages/agreement-email-sender/.env +++ b/packages/agreement-email-sender/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="agreement-email-sender--group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH=true AGREEMENT_TOPIC="event-store.agreement.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" diff --git a/packages/agreement-platformstate-writer/.env b/packages/agreement-platformstate-writer/.env index 4d789cf37c..e585e9bbe0 100644 --- a/packages/agreement-platformstate-writer/.env +++ b/packages/agreement-platformstate-writer/.env @@ -9,4 +9,4 @@ AWS_CONFIG_FILE=aws.config.local TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/agreement-process/aws.config.local b/packages/agreement-process/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/agreement-process/aws.config.local +++ b/packages/agreement-process/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/agreement-readmodel-writer/.env b/packages/agreement-readmodel-writer/.env index 59843f8ed3..01f629a874 100644 --- a/packages/agreement-readmodel-writer/.env +++ b/packages/agreement-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/anac-certified-attributes-importer/aws.config.local b/packages/anac-certified-attributes-importer/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/anac-certified-attributes-importer/aws.config.local +++ b/packages/anac-certified-attributes-importer/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/attribute-registry-readmodel-writer/.env b/packages/attribute-registry-readmodel-writer/.env index fd326b0244..d1eee2fad3 100644 --- a/packages/attribute-registry-readmodel-writer/.env +++ b/packages/attribute-registry-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/authorization-updater/.env b/packages/authorization-updater/.env index 9c1b2b0940..0b401234c2 100644 --- a/packages/authorization-updater/.env +++ b/packages/authorization-updater/.env @@ -8,7 +8,7 @@ CATALOG_TOPIC="event-store.catalog.events" AGREEMENT_TOPIC="event-store.agreement.events" PURPOSE_TOPIC="event-store.purpose.events" AUTHORIZATION_TOPIC="event-store.authorization.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" diff --git a/packages/authorization-updater/aws.config.local b/packages/authorization-updater/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/authorization-updater/aws.config.local +++ b/packages/authorization-updater/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/backend-for-frontend/aws.config.local b/packages/backend-for-frontend/aws.config.local index 4a2fef9936..5d3103957c 100644 --- a/packages/backend-for-frontend/aws.config.local +++ b/packages/backend-for-frontend/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index 46444e0dc0..ed13803846 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -49,7 +49,7 @@ describe("utils tests", async () => { } const dynamoDBClient = new DynamoDBClient({ credentials: { accessKeyId: "key", secretAccessKey: "secret" }, - region: "eu-central-1", + region: "eu-south-1", endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, }); beforeEach(async () => { diff --git a/packages/catalog-process/aws.config.local b/packages/catalog-process/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/catalog-process/aws.config.local +++ b/packages/catalog-process/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/catalog-readmodel-writer/.env b/packages/catalog-readmodel-writer/.env index 837d78ede5..06d51acd5b 100644 --- a/packages/catalog-readmodel-writer/.env +++ b/packages/catalog-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/client-readmodel-writer/.env b/packages/client-readmodel-writer/.env index 06c31f2515..5423be0ba1 100644 --- a/packages/client-readmodel-writer/.env +++ b/packages/client-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/commons-test/aws.config.local b/packages/commons-test/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/commons-test/aws.config.local +++ b/packages/commons-test/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/commons-test/src/containerTestUtils.ts b/packages/commons-test/src/containerTestUtils.ts index d74b08d248..3f76738c80 100644 --- a/packages/commons-test/src/containerTestUtils.ts +++ b/packages/commons-test/src/containerTestUtils.ts @@ -85,7 +85,7 @@ export const minioContainer = (config: S3Config): GenericContainer => .withEnvironment({ MINIO_ROOT_USER: "testawskey", MINIO_ROOT_PASSWORD: "testawssecret", - MINIO_SITE_REGION: "eu-central-1", + MINIO_SITE_REGION: "eu-south-1", }) .withEntrypoint(["sh", "-c"]) .withCommand([ diff --git a/packages/commons-test/test/SES.test.ts b/packages/commons-test/test/SES.test.ts index 72a1a2a0e4..608e1acc1a 100644 --- a/packages/commons-test/test/SES.test.ts +++ b/packages/commons-test/test/SES.test.ts @@ -15,7 +15,7 @@ describe("initSesMailManager", () => { sesMock.on(SendEmailCommand).resolves({}); const awsSesConfig: AWSSesConfig = { - awsRegion: "eu-central-1", + awsRegion: "eu-south-1", awsSesEndpoint: undefined, }; const emailManager = initSesMailManager(awsSesConfig); diff --git a/packages/compute-agreements-consumer/.env b/packages/compute-agreements-consumer/.env index 785ea53132..9fcc7fca87 100644 --- a/packages/compute-agreements-consumer/.env +++ b/packages/compute-agreements-consumer/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="compute-agreements-consumer-group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" TENANT_TOPIC="event-store.tenant.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" AGREEMENT_PROCESS_URL="http://0.0.0.0:3000" diff --git a/packages/compute-agreements-consumer/aws.config.local b/packages/compute-agreements-consumer/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/compute-agreements-consumer/aws.config.local +++ b/packages/compute-agreements-consumer/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/datalake-data-export/aws.config.local b/packages/datalake-data-export/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/datalake-data-export/aws.config.local +++ b/packages/datalake-data-export/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/dtd-catalog-exporter/.env b/packages/dtd-catalog-exporter/.env index d2a817544f..6eccaff37c 100644 --- a/packages/dtd-catalog-exporter/.env +++ b/packages/dtd-catalog-exporter/.env @@ -15,5 +15,5 @@ DTD_CATALOG_STORAGE_PATH="dtd-catalog" DTD_CATALOG_FILENAME="dtd-catalog.json" AWS_CONFIG_FILE=aws.config.local -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/dtd-catalog-exporter/aws.config.local b/packages/dtd-catalog-exporter/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/dtd-catalog-exporter/aws.config.local +++ b/packages/dtd-catalog-exporter/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/eservice-descriptors-archiver/.env b/packages/eservice-descriptors-archiver/.env index 39f9cd10f5..97ebfdbd82 100644 --- a/packages/eservice-descriptors-archiver/.env +++ b/packages/eservice-descriptors-archiver/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="eservice-descriptors-archiver-group-local" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" AGREEMENT_TOPIC="event-store.agreement.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" diff --git a/packages/eservice-descriptors-archiver/aws.config.local b/packages/eservice-descriptors-archiver/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/eservice-descriptors-archiver/aws.config.local +++ b/packages/eservice-descriptors-archiver/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/ivass-certified-attributes-importer/aws.config.local b/packages/ivass-certified-attributes-importer/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/ivass-certified-attributes-importer/aws.config.local +++ b/packages/ivass-certified-attributes-importer/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/kafka-iam-auth/README.md b/packages/kafka-iam-auth/README.md index 55e0b7b5e2..eb950abe4d 100644 --- a/packages/kafka-iam-auth/README.md +++ b/packages/kafka-iam-auth/README.md @@ -30,21 +30,22 @@ await runConsumer(config, topicName, processMessageHandler); ``` ## Local Testing + To simulate Kafaka topic consuming and executing a real SALS authentication with AWS, you need to connect your consumer to specific topic presents in dev environment. You must put the following variables in your consumer .env file to simulate the same credential provisioning executed by service in Kubernates pod: ```bash AWS_WEB_IDENTITY_TOKEN_FILE="{TOKE_FILE_PATH}" AWS_ROLE_ARN="arn:aws:iam::{ID}:role/interop-be-{SERVICE}-consumer-refactor-dev" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" AWS_STS_REGIONAL_ENDPOINTS="regional" -AWS_DEFAULT_REGION="eu-central-1" +AWS_DEFAULT_REGION="eu-south-1" ``` Replace all placeholders {...} with desired configurations. Token file should contains a valid token retrieved from AWS, by the way all of those variables can be found inspecting pod in dev cluster. - ## Credits + This project uses code from the [Original Repository](/~https://github.com/jmaver-plume/kafkajs-msk-iam-authentication-mechanism), which is licensed under the MIT License. We are grateful to the original authors and contributors for their work. diff --git a/packages/key-readmodel-writer/.env b/packages/key-readmodel-writer/.env index 297b233277..5282be8508 100644 --- a/packages/key-readmodel-writer/.env +++ b/packages/key-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/notifier-seeder/.env b/packages/notifier-seeder/.env index 526efb796e..b64a315f86 100644 --- a/packages/notifier-seeder/.env +++ b/packages/notifier-seeder/.env @@ -8,5 +8,5 @@ CATALOG_TOPIC="event-store.catalog.events" PURPOSE_TOPIC="event-store.purpose.events" AWS_CONFIG_FILE=aws.config.local -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" NOTIFICATION_QUEUE_URL="http://localhost:9324/000000000000/sqsLocalQueue.fifo" diff --git a/packages/notifier-seeder/aws.config.local b/packages/notifier-seeder/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/notifier-seeder/aws.config.local +++ b/packages/notifier-seeder/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/one-trust-notices/aws.config.local b/packages/one-trust-notices/aws.config.local index 4a3a746c5a..06a4c18070 100644 --- a/packages/one-trust-notices/aws.config.local +++ b/packages/one-trust-notices/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawskey -region=eu-central-1 \ No newline at end of file +region=eu-south-1 diff --git a/packages/producer-key-events-writer/.env b/packages/producer-key-events-writer/.env index a27c11516a..0a791d9188 100644 --- a/packages/producer-key-events-writer/.env +++ b/packages/producer-key-events-writer/.env @@ -13,4 +13,4 @@ KAFKA_GROUP_ID="producer-key-events-group" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" AUTHORIZATION_TOPIC="event-store.authorization.events" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/producer-key-readmodel-writer/.env b/packages/producer-key-readmodel-writer/.env index 1ce4ba2520..c7249b87bf 100644 --- a/packages/producer-key-readmodel-writer/.env +++ b/packages/producer-key-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/producer-keychain-readmodel-writer/.env b/packages/producer-keychain-readmodel-writer/.env index 37dfb3febf..08f3bc87cb 100644 --- a/packages/producer-keychain-readmodel-writer/.env +++ b/packages/producer-keychain-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/purpose-process/aws.config.local b/packages/purpose-process/aws.config.local index 6b65b27871..34826a60e2 100644 --- a/packages/purpose-process/aws.config.local +++ b/packages/purpose-process/aws.config.local @@ -1,4 +1,4 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 diff --git a/packages/purpose-readmodel-writer/.env b/packages/purpose-readmodel-writer/.env index de8cdcc8bc..9006d7735e 100644 --- a/packages/purpose-readmodel-writer/.env +++ b/packages/purpose-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" diff --git a/packages/selfcare-onboarding-consumer/.env b/packages/selfcare-onboarding-consumer/.env index ed0fe628e1..06bc52ac9d 100644 --- a/packages/selfcare-onboarding-consumer/.env +++ b/packages/selfcare-onboarding-consumer/.env @@ -7,7 +7,7 @@ KAFKA_BROKERS="localhost:9092" KAFKA_BROKER_CONNECTION_STRING="placeholder" SELFCARE_TOPIC="placeholder.selfcare.topic" TOPIC_STARTING_OFFSET="earliest" -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" RESET_CONSUMER_OFFSETS="false" diff --git a/packages/selfcare-onboarding-consumer/aws.config.local b/packages/selfcare-onboarding-consumer/aws.config.local index 9c57dc948e..8dfc0e7f55 100644 --- a/packages/selfcare-onboarding-consumer/aws.config.local +++ b/packages/selfcare-onboarding-consumer/aws.config.local @@ -1,7 +1,7 @@ [default] aws_access_key_id=testawskey aws_secret_access_key=testawssecret -region=eu-central-1 +region=eu-south-1 services=local [services local] diff --git a/packages/tenant-readmodel-writer/.env b/packages/tenant-readmodel-writer/.env index 1ed07e0ba1..3b9d5ab94a 100644 --- a/packages/tenant-readmodel-writer/.env +++ b/packages/tenant-readmodel-writer/.env @@ -10,4 +10,4 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 -AWS_REGION="eu-central-1" +AWS_REGION="eu-south-1" From b3db1fea45fe20d39e1cb21f4da2794c2b046a9a Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 30 Oct 2024 14:10:43 +0100 Subject: [PATCH 002/126] IMN-842 - Removing uuid and using crypto (#1127) --- packages/agreement-email-sender/package.json | 2 - .../agreement-outbound-writer/package.json | 2 - .../package.json | 2 - packages/agreement-process/package.json | 2 - .../agreement-readmodel-writer/package.json | 2 - .../package.json | 2 - .../attribute-registry-process/package.json | 2 - ...tributeRegistryService.integration.test.ts | 10 +- packages/authorization-process/package.json | 1 - packages/authorization-updater/package.json | 2 - packages/backend-for-frontend/package.json | 2 - packages/catalog-outbound-writer/package.json | 2 - .../catalog-platformstate-writer/package.json | 2 - packages/catalog-process/package.json | 2 - .../catalog-readmodel-writer/package.json | 2 - packages/client-readmodel-writer/package.json | 2 - packages/commons-test/package.json | 2 - packages/commons/package.json | 2 - packages/commons/src/context/context.ts | 4 +- .../compute-agreements-consumer/package.json | 2 - packages/datalake-data-export/package.json | 2 - .../package.json | 2 - .../package.json | 4 - packages/key-readmodel-writer/package.json | 2 - packages/models/package.json | 2 - packages/models/src/brandedIds.ts | 4 +- packages/notifier-seeder/package.json | 2 - .../agreementEventNotificationMessage.ts | 4 +- .../authorizationEventNotificationMessage.ts | 4 +- .../catalogItemEventNotificationMessage.ts | 6 +- .../purposeEventNotificationMessage.ts | 4 +- .../notificationMessage.integration.test.ts | 11 +- .../test/queueManager.integration.test.ts | 6 +- packages/pn-consumers/package.json | 2 - .../test/readModelQueriesService.test.ts | 10 +- .../producer-key-events-writer/package.json | 2 - .../package.json | 2 - .../package.json | 2 - packages/purpose-outbound-writer/package.json | 2 - .../purpose-platformstate-writer/package.json | 2 - .../purpose-readmodel-writer/package.json | 2 - .../selfcare-onboarding-consumer/package.json | 2 - packages/tenant-outbound-writer/package.json | 2 - packages/tenant-process/package.json | 2 - pnpm-lock.yaml | 208 ------------------ 45 files changed, 31 insertions(+), 309 deletions(-) diff --git a/packages/agreement-email-sender/package.json b/packages/agreement-email-sender/package.json index 7d5ffffa8b..bfb9359b03 100644 --- a/packages/agreement-email-sender/package.json +++ b/packages/agreement-email-sender/package.json @@ -25,7 +25,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "cpx2": "7.0.1", "axios": "1.7.4", "pagopa-interop-commons-test": "workspace:*", @@ -33,7 +32,6 @@ "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 55023519f9..29903c6541 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/agreement-platformstate-writer/package.json b/packages/agreement-platformstate-writer/package.json index 11f0fee562..4774847616 100644 --- a/packages/agreement-platformstate-writer/package.json +++ b/packages/agreement-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index 96db2dc22a..980c7b3721 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -23,7 +23,6 @@ "@pagopa/eslint-config": "3.0.0", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "cpx2": "7.0.1", "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", @@ -47,7 +46,6 @@ "pagopa-interop-api-clients": "workspace:*", "pagopa-interop-agreement-lifecycle": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 1f5be6d7ea..9fb6785dcc 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0", "@anatine/zod-mock": "3.13.4" }, diff --git a/packages/anac-certified-attributes-importer/package.json b/packages/anac-certified-attributes-importer/package.json index f318036f2e..a78f5ad664 100644 --- a/packages/anac-certified-attributes-importer/package.json +++ b/packages/anac-certified-attributes-importer/package.json @@ -24,7 +24,6 @@ "@types/ssh2-sftp-client": "9.0.4", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "@types/uuid": "9.0.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", @@ -33,7 +32,6 @@ "dependencies": { "csv": "^6.3.2", "dotenv-flow": "4.1.0", - "uuid": "10.0.0", "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", diff --git a/packages/attribute-registry-process/package.json b/packages/attribute-registry-process/package.json index d7387f2bd6..9725a194c8 100644 --- a/packages/attribute-registry-process/package.json +++ b/packages/attribute-registry-process/package.json @@ -24,7 +24,6 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -46,7 +45,6 @@ "pagopa-interop-api-clients": "workspace:*", "pg-promise": "11.8.0", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts index 3291374623..1064f5ddd2 100644 --- a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts +++ b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ +import { randomUUID } from "crypto"; import { describe, expect, it } from "vitest"; import { decodeProtobufPayload, @@ -7,7 +8,6 @@ import { getMockAuthData, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; -import { v4 as uuidv4 } from "uuid"; import { Attribute, AttributeAddedV1, @@ -214,7 +214,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; @@ -274,7 +274,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; @@ -342,7 +342,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; @@ -402,7 +402,7 @@ describe("database test", () => { features: [ { type: "PersistentCertifier", - certifierId: uuidv4(), + certifierId: randomUUID(), }, ], }; diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json index a71f8e48bf..27f0127472 100644 --- a/packages/authorization-process/package.json +++ b/packages/authorization-process/package.json @@ -22,7 +22,6 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", diff --git a/packages/authorization-updater/package.json b/packages/authorization-updater/package.json index 2338c136e4..d703d3b783 100644 --- a/packages/authorization-updater/package.json +++ b/packages/authorization-updater/package.json @@ -25,7 +25,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", @@ -45,7 +44,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/backend-for-frontend/package.json b/packages/backend-for-frontend/package.json index 02f8500104..7044893c23 100644 --- a/packages/backend-for-frontend/package.json +++ b/packages/backend-for-frontend/package.json @@ -24,7 +24,6 @@ "@types/multer": "1.4.11", "@types/node": "20.14.6", "@types/qs": "6.9.15", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -51,7 +50,6 @@ "pagopa-interop-models": "workspace:*", "qs": "6.12.3", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "yaml": "2.4.5", "zod": "3.23.8" } diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index a5a8e5cbe6..8e19ced827 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/catalog-platformstate-writer/package.json b/packages/catalog-platformstate-writer/package.json index a1f3084e60..4a720b21c3 100644 --- a/packages/catalog-platformstate-writer/package.json +++ b/packages/catalog-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/catalog-process/package.json b/packages/catalog-process/package.json index 6c28f0d06a..769b1246b8 100644 --- a/packages/catalog-process/package.json +++ b/packages/catalog-process/package.json @@ -25,7 +25,6 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "pg-promise": "11.8.0", @@ -47,7 +46,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/catalog-readmodel-writer/package.json b/packages/catalog-readmodel-writer/package.json index d4fbdc3efc..8a6e112fd4 100644 --- a/packages/catalog-readmodel-writer/package.json +++ b/packages/catalog-readmodel-writer/package.json @@ -22,14 +22,12 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/client-readmodel-writer/package.json b/packages/client-readmodel-writer/package.json index 2e2aa6b8c8..6b5a59464d 100644 --- a/packages/client-readmodel-writer/package.json +++ b/packages/client-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/commons-test/package.json b/packages/commons-test/package.json index dde6a915e0..c8acc06d33 100644 --- a/packages/commons-test/package.json +++ b/packages/commons-test/package.json @@ -26,7 +26,6 @@ "@protobuf-ts/runtime": "2.9.4", "@testcontainers/postgresql": "10.9.0", "@types/jsonwebtoken": "9.0.6", - "@types/uuid": "9.0.8", "@zodios/core": "10.9.6", "aws-sdk-client-mock": "4.0.1", "axios": "1.7.4", @@ -38,7 +37,6 @@ "testcontainers": "10.9.0", "ts-pattern": "5.2.0", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0", "zod": "3.23.8" } diff --git a/packages/commons/package.json b/packages/commons/package.json index 237c7ee534..a2d1cf32a8 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -42,7 +42,6 @@ "rate-limiter-flexible": "5.0.3", "redis": "4.6.15", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "winston": "3.13.0", "zod": "3.23.8", "zod-validation-error": "3.3.0" @@ -52,7 +51,6 @@ "@types/jsonwebtoken": "9.0.6", "@types/node": "20.14.6", "@types/nodemailer": "6.4.15", - "@types/uuid": "9.0.8", "cpx2": "7.0.1", "eslint": "8.57.0", "prettier": "2.8.8", diff --git a/packages/commons/src/context/context.ts b/packages/commons/src/context/context.ts index 847f9d385c..e37a83d84b 100644 --- a/packages/commons/src/context/context.ts +++ b/packages/commons/src/context/context.ts @@ -1,9 +1,9 @@ import { constants } from "http2"; +import { randomUUID } from "crypto"; import { ZodiosRouterContextRequestHandler, zodiosContext, } from "@zodios/express"; -import { v4 as uuidv4 } from "uuid"; import { z } from "zod"; import { CorrelationId, @@ -63,7 +63,7 @@ export const contextMiddleware = setCtx(correlationIdHeader); } else { - setCtx(uuidv4()); + setCtx(randomUUID()); } return next(); diff --git a/packages/compute-agreements-consumer/package.json b/packages/compute-agreements-consumer/package.json index a8c15c79d7..b2b6d22bb8 100644 --- a/packages/compute-agreements-consumer/package.json +++ b/packages/compute-agreements-consumer/package.json @@ -20,7 +20,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5" @@ -33,7 +32,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/datalake-data-export/package.json b/packages/datalake-data-export/package.json index 6ecf307f90..d9ab0456b6 100644 --- a/packages/datalake-data-export/package.json +++ b/packages/datalake-data-export/package.json @@ -19,7 +19,6 @@ "license": "Apache-2.0", "devDependencies": { "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "@pagopa/eslint-config": "3.0.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", @@ -33,7 +32,6 @@ "dotenv-flow": "4.1.0", "date-fns": "3.6.0", "mongodb": "6.7.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/eservice-descriptors-archiver/package.json b/packages/eservice-descriptors-archiver/package.json index 94ca71141d..c81de847eb 100644 --- a/packages/eservice-descriptors-archiver/package.json +++ b/packages/eservice-descriptors-archiver/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "openapi-zod-client": "1.18.1", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -40,7 +39,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/ivass-certified-attributes-importer/package.json b/packages/ivass-certified-attributes-importer/package.json index c60e0205ad..a4866577fa 100644 --- a/packages/ivass-certified-attributes-importer/package.json +++ b/packages/ivass-certified-attributes-importer/package.json @@ -23,9 +23,7 @@ "@types/node": "20.14.6", "@types/ssh2-sftp-client": "9.0.4", "pagopa-interop-commons-test": "workspace:*", - "@types/uuid": "9.0.8", "@types/adm-zip": "0.5.5", - "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", @@ -33,8 +31,6 @@ "vitest": "1.6.0" }, "dependencies": { - "ts-pattern": "5.2.0", - "uuid": "10.0.0", "@aws-sdk/client-s3": "3.387.0", "adm-zip": "0.5.15", "axios": "1.7.4", diff --git a/packages/key-readmodel-writer/package.json b/packages/key-readmodel-writer/package.json index 761b9c59da..af4f12b037 100644 --- a/packages/key-readmodel-writer/package.json +++ b/packages/key-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/models/package.json b/packages/models/package.json index d3ce8ffb63..f3ab5d994d 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -26,9 +26,7 @@ "@protobuf-ts/protoc": "2.9.4", "@protobuf-ts/runtime": "2.9.4", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8", "zod-validation-error": "3.3.0" }, diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index a9a306ff65..d2e9470fb0 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -1,5 +1,5 @@ +import { randomUUID } from "crypto"; import { z } from "zod"; -import { v4 as uuidv4 } from "uuid"; export const CorrelationId = z.string().brand("CorrelationId"); export type CorrelationId = z.infer; @@ -159,7 +159,7 @@ type IDS = // it infers the type of the ID based on how is used the result // the 'as' is used to cast the uuid string to the inferred type export function generateId(): T { - return uuidv4() as T; + return randomUUID() as T; } // This function is used to get a branded ID from a string diff --git a/packages/notifier-seeder/package.json b/packages/notifier-seeder/package.json index f06224c2b9..ac3c5213a5 100644 --- a/packages/notifier-seeder/package.json +++ b/packages/notifier-seeder/package.json @@ -29,7 +29,6 @@ "ts-node": "10.9.2", "typescript": "5.4.5", "kafkajs": "2.2.4", - "@types/uuid": "9.0.8", "mkdirp": "3.0.1", "pagopa-interop-commons-test": "workspace:*", "testcontainers": "10.9.0", @@ -45,7 +44,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts b/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts index 97f1ad2b34..5e36a96556 100644 --- a/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/agreement/agreementEventNotificationMessage.ts @@ -1,6 +1,6 @@ +import { randomUUID } from "crypto"; import { AgreementEventEnvelopeV2 } from "pagopa-interop-models"; import { match } from "ts-pattern"; -import { v4 as uuidv4 } from "uuid"; import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { AgreementEventNotification } from "./agreementEventNotification.js"; @@ -41,7 +41,7 @@ export const buildAgreementMessage = ( event: AgreementEventEnvelopeV2, agreementEvent: AgreementEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts b/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts index eb8f23a3ad..2a915d3141 100644 --- a/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/authorization/authorizationEventNotificationMessage.ts @@ -1,5 +1,5 @@ +import { randomUUID } from "crypto"; import { match } from "ts-pattern"; -import { v4 as uuidv4 } from "uuid"; import { AuthorizationEventEnvelopeV2 } from "pagopa-interop-models"; import { QueueMessage } from "../../queue-manager/queueMessage.js"; import { AuthorizationEventNotification } from "./authorizationEventNotification.js"; @@ -34,7 +34,7 @@ export const buildAuthorizationMessage = ( event: AuthorizationEventEnvelopeV2, authorizationEvent: AuthorizationEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts index 03a4a950da..cc05355565 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts @@ -1,6 +1,6 @@ +import { randomUUID } from "crypto"; 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 { CatalogItemEventNotification } from "./catalogItemEventNotification.js"; @@ -59,7 +59,7 @@ export const eventV2TypeMapper = ( ) .exhaustive(); -/* +/* This method is used to build a message for catalog events, that to be sent to the notify queue, it will be used to mantains compatibility with the old version of queue consumers. Related issue https://pagopa.atlassian.net/browse/IMN-67 @@ -68,7 +68,7 @@ export const buildCatalogMessage = ( event: EServiceEventEnvelopeV2, catalogItemEvent: CatalogItemEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts index a426ec90ee..2b82fb7b20 100644 --- a/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/purpose/purposeEventNotificationMessage.ts @@ -1,6 +1,6 @@ +import { randomUUID } from "crypto"; 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"; @@ -46,7 +46,7 @@ export const buildPurposeMessage = ( event: PurposeEventEnvelopeV2, purposeEvent: PurposeEventNotification ): QueueMessage => ({ - messageUUID: uuidv4(), + messageUUID: randomUUID(), eventJournalPersistenceId: event.stream_id, eventJournalSequenceNumber: event.version, eventTimestamp: Number(event.log_date), diff --git a/packages/notifier-seeder/test/notificationMessage.integration.test.ts b/packages/notifier-seeder/test/notificationMessage.integration.test.ts index 1814fd3f1e..1c82100303 100644 --- a/packages/notifier-seeder/test/notificationMessage.integration.test.ts +++ b/packages/notifier-seeder/test/notificationMessage.integration.test.ts @@ -1,5 +1,5 @@ +import { randomUUID } from "crypto"; import { describe, expect, it, vi } from "vitest"; - import { Agreement, AgreementAddedV2, @@ -24,7 +24,6 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { genericLogger } from "pagopa-interop-commons"; -import { v4 } from "uuid"; import { getMockAgreement, getMockClient, @@ -135,7 +134,7 @@ describe("Notification tests", async () => { sequence_num: 1, stream_id: "d27f668f-630b-4889-a97f-2b7e39b24188", version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "EServiceDescriptorSuspended", @@ -160,7 +159,7 @@ describe("Notification tests", async () => { sequence_num: 2, stream_id: mockPurpose.id, version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "PurposeAdded", @@ -194,7 +193,7 @@ describe("Notification tests", async () => { sequence_num: 2, stream_id: mockAgreement.id, version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "AgreementAdded", @@ -222,7 +221,7 @@ describe("Notification tests", async () => { sequence_num: 3, stream_id: mockClient.id, version: 1, - correlation_id: v4(), + correlation_id: randomUUID(), log_date: new Date(), event_version: 2, type: "ClientKeyAdded", diff --git a/packages/notifier-seeder/test/queueManager.integration.test.ts b/packages/notifier-seeder/test/queueManager.integration.test.ts index 2651d8534d..4b5c89c856 100644 --- a/packages/notifier-seeder/test/queueManager.integration.test.ts +++ b/packages/notifier-seeder/test/queueManager.integration.test.ts @@ -1,6 +1,6 @@ /* eslint-disable functional/immutable-data */ /* eslint-disable functional/no-let */ -import { v4 as uuidv4 } from "uuid"; +import { randomUUID } from "crypto"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -18,7 +18,7 @@ describe("FileManager tests", async () => { it("should send a message to the queue and receive it back", async () => { await queueWriter.send( { - messageUUID: uuidv4(), + messageUUID: randomUUID(), kind: "TestMessageKind", eventJournalPersistenceId: "test-persistence-id", eventJournalSequenceNumber: 0, @@ -47,7 +47,7 @@ describe("FileManager tests", async () => { await expect( nonExistingQueueWriter.send( { - messageUUID: uuidv4(), + messageUUID: randomUUID(), kind: "TestMessageKind", eventJournalPersistenceId: "test-persistence-id", eventJournalSequenceNumber: 0, diff --git a/packages/pn-consumers/package.json b/packages/pn-consumers/package.json index c741dfba1a..1d016ccfe2 100644 --- a/packages/pn-consumers/package.json +++ b/packages/pn-consumers/package.json @@ -23,8 +23,6 @@ "@types/node": "20.14.6", "@types/nodemailer": "6.4.9", "pagopa-interop-commons-test": "workspace:*", - "@types/uuid": "9.0.8", - "uuid": "10.0.0", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", diff --git a/packages/pn-consumers/test/readModelQueriesService.test.ts b/packages/pn-consumers/test/readModelQueriesService.test.ts index 63bbea7c55..5b11bb3f9e 100644 --- a/packages/pn-consumers/test/readModelQueriesService.test.ts +++ b/packages/pn-consumers/test/readModelQueriesService.test.ts @@ -1,4 +1,5 @@ /* eslint-disable functional/no-let */ +import { randomUUID } from "crypto"; import { describe, expect, it, afterEach, inject } from "vitest"; import { getMockPurpose, @@ -7,15 +8,14 @@ import { setupTestContainersVitest, } from "pagopa-interop-commons-test/index.js"; import { AttributeId, TenantId, unsafeBrandId } from "pagopa-interop-models"; -import { v4 as uuidv4 } from "uuid"; import { ReadModelQueriesClient } from "../src/services/readModelQueriesService.js"; const PN_ESERVICE_ID_MOCK = "4747d063-0d9c-4a5d-b143-9f2fdc4d7f22"; const COMUNI_E_LORO_CONSORZI_E_ASSOCIAZIONI_ATTRIBUTE_ID_MOCK = "5ec5dd81-ff71-4af8-974b-4190eb8347bf"; -const TENANT_COMUNE_ID = uuidv4(); -const TENANT_NON_COMUNE_ID = uuidv4(); +const TENANT_COMUNE_ID = randomUUID(); +const TENANT_NON_COMUNE_ID = randomUUID(); export const { cleanup, readModelRepository, postgresDB, fileManager } = await setupTestContainersVitest( @@ -75,7 +75,7 @@ describe("MetricsManager", () => { consumerId: unsafeBrandId(TENANT_COMUNE_ID), versions: [ { - id: uuidv4(), + id: randomUUID(), state: "Active", dailyCalls: 1, createdAt: new Date(), @@ -90,7 +90,7 @@ describe("MetricsManager", () => { consumerId: unsafeBrandId(TENANT_NON_COMUNE_ID), versions: [ { - id: uuidv4(), + id: randomUUID(), state: "Active", dailyCalls: 1, createdAt: new Date(), diff --git a/packages/producer-key-events-writer/package.json b/packages/producer-key-events-writer/package.json index 5c9843fb69..8b1c51a2d5 100644 --- a/packages/producer-key-events-writer/package.json +++ b/packages/producer-key-events-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", @@ -37,7 +36,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/producer-key-readmodel-writer/package.json b/packages/producer-key-readmodel-writer/package.json index 00e45346b1..da8bfaa6d4 100644 --- a/packages/producer-key-readmodel-writer/package.json +++ b/packages/producer-key-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/producer-keychain-readmodel-writer/package.json b/packages/producer-keychain-readmodel-writer/package.json index 2e3926b675..d73caa1024 100644 --- a/packages/producer-keychain-readmodel-writer/package.json +++ b/packages/producer-keychain-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index e34f3f1141..5b2830f708 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/purpose-platformstate-writer/package.json b/packages/purpose-platformstate-writer/package.json index ea2a130497..b17c00dde4 100644 --- a/packages/purpose-platformstate-writer/package.json +++ b/packages/purpose-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/purpose-readmodel-writer/package.json b/packages/purpose-readmodel-writer/package.json index 47ec8edf85..cbdabd96f1 100644 --- a/packages/purpose-readmodel-writer/package.json +++ b/packages/purpose-readmodel-writer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", @@ -38,7 +37,6 @@ "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/selfcare-onboarding-consumer/package.json b/packages/selfcare-onboarding-consumer/package.json index eaf9394332..e1eb6bf376 100644 --- a/packages/selfcare-onboarding-consumer/package.json +++ b/packages/selfcare-onboarding-consumer/package.json @@ -22,7 +22,6 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "prettier": "2.8.8", "ts-node": "10.9.2", "typescript": "5.4.5", @@ -37,7 +36,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 2dacaf93a7..190a113491 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -20,13 +20,11 @@ "@anatine/zod-mock": "3.13.4", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", "ts-node": "10.9.2", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/tenant-process/package.json b/packages/tenant-process/package.json index afb5e3112c..1ec9be60ad 100644 --- a/packages/tenant-process/package.json +++ b/packages/tenant-process/package.json @@ -30,7 +30,6 @@ "vitest": "1.6.0" }, "dependencies": { - "@types/uuid": "9.0.8", "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", "axios": "1.7.4", @@ -42,7 +41,6 @@ "pagopa-interop-models": "workspace:*", "pagopa-interop-api-clients": "workspace:*", "ts-pattern": "5.2.0", - "uuid": "10.0.0", "zod": "3.23.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d5ee42b992..9dd9ae6c53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,9 +48,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 axios: specifier: 1.7.4 version: 1.7.4 @@ -72,9 +69,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -140,9 +134,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -158,9 +149,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -207,9 +195,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -225,9 +210,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -267,9 +249,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -286,9 +265,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 cpx2: specifier: 7.0.1 version: 7.0.1 @@ -359,9 +335,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -377,9 +350,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -404,9 +374,6 @@ importers: ssh2-sftp-client: specifier: ^9.1.0 version: 9.1.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -420,9 +387,6 @@ importers: '@types/ssh2-sftp-client': specifier: 9.0.4 version: 9.0.4 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -599,9 +563,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -624,9 +585,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -758,9 +716,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -812,9 +767,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -825,9 +777,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -903,9 +852,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 yaml: specifier: 2.4.5 version: 2.4.5 @@ -931,9 +877,6 @@ importers: '@types/qs': specifier: 6.9.15 version: 6.9.15 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -995,9 +938,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1013,9 +953,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1062,9 +999,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1080,9 +1014,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1122,9 +1053,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1147,9 +1075,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1211,9 +1136,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1232,9 +1154,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1305,9 +1224,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1318,9 +1234,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1408,9 +1321,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 winston: specifier: 3.13.0 version: 3.13.0 @@ -1433,9 +1343,6 @@ importers: '@types/nodemailer': specifier: 6.4.15 version: 6.4.15 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 cpx2: specifier: 7.0.1 version: 7.0.1 @@ -1475,9 +1382,6 @@ importers: '@types/jsonwebtoken': specifier: 9.0.6 version: 9.0.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -1511,9 +1415,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1544,9 +1445,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1557,9 +1455,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 prettier: specifier: 2.8.8 version: 2.8.8 @@ -1587,9 +1482,6 @@ importers: pagopa-interop-models: specifier: workspace:* version: link:../models - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1600,9 +1492,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1691,9 +1580,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1704,9 +1590,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -1816,9 +1699,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1835,9 +1715,6 @@ importers: '@types/ssh2-sftp-client': specifier: 9.0.4 version: 9.0.4 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1902,9 +1779,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1915,9 +1789,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1951,15 +1822,9 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2012,9 +1877,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2025,9 +1887,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -2132,9 +1991,6 @@ importers: '@types/nodemailer': specifier: 6.4.9 version: 6.4.9 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2150,9 +2006,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2180,9 +2033,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2193,9 +2043,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2235,9 +2082,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2248,9 +2092,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2293,9 +2134,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2306,9 +2144,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2370,9 +2205,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2388,9 +2220,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2437,9 +2266,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -2455,9 +2281,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2564,9 +2387,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2577,9 +2397,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2625,9 +2442,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2638,9 +2452,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 prettier: specifier: 2.8.8 version: 2.8.8 @@ -2696,9 +2507,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2714,18 +2522,12 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) packages/tenant-process: dependencies: - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -2759,9 +2561,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -8134,9 +7933,6 @@ packages: resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} dev: false - /@types/uuid@9.0.8: - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - /@types/webidl-conversions@7.0.3: resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} dev: false @@ -13089,10 +12885,6 @@ packages: engines: {node: '>= 0.4.0'} dev: false - /uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true From b9ffd7ee63657002eb7af7daa9cba23acd789734 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 31 Oct 2024 15:09:20 +0100 Subject: [PATCH 003/126] IMN-792 Add events service v2 in agreement-platformstate-writer (#987) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV2.ts | 293 +- .../src/utils.ts | 448 ++- .../consumerServiceV2.integration.test.ts | 3137 +++++++++++++++++ .../test/utils.test.ts | 650 ++++ .../test/utils.ts | 11 + .../test/utils.test.ts | 2 - packages/commons-test/src/testUtils.ts | 21 + .../src/tokenGenerationReadmodelUtils.ts | 272 +- 8 files changed, 4749 insertions(+), 85 deletions(-) create mode 100644 packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts create mode 100644 packages/agreement-platformstate-writer/test/utils.test.ts create mode 100644 packages/agreement-platformstate-writer/test/utils.ts diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index 53cb09bd99..caa711eee0 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -1,27 +1,292 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { AgreementEventEnvelopeV2 } from "pagopa-interop-models"; +import { + Agreement, + AgreementEventEnvelopeV2, + AgreementV2, + fromAgreementV2, + genericInternalError, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, +} from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { + agreementStateToItemState, + deleteAgreementEntry, + readAgreementEntry, + readCatalogEntry, + updateAgreementStateInPlatformStatesEntry, + updateAgreementStateOnTokenStates, + updateAgreementStateAndDescriptorInfoOnTokenStates, + writeAgreementEntry, + isLatestAgreement, +} from "./utils.js"; export async function handleMessageV2( message: AgreementEventEnvelopeV2, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "AgreementActivated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + + const existingAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (existingAgreementEntry) { + if (existingAgreementEntry.version > msg.version) { + // Stops processing if the message is older than the agreement entry + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version + ); + } + } else { + if (!agreement.stamps.activation) { + throw genericInternalError( + "An activated agreement should have activation stamp" + ); + } + const agreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msg.version, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + agreement.stamps.activation.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(agreementEntry, dynamoDBClient); + } + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + // token-generation-states + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } + }) .with( - { type: "AgreementAdded" }, - { type: "AgreementDeleted" }, - { type: "DraftAgreementUpdated" }, - { type: "AgreementSubmitted" }, - { type: "AgreementActivated" }, { type: "AgreementUnsuspendedByProducer" }, { type: "AgreementUnsuspendedByConsumer" }, { type: "AgreementUnsuspendedByPlatform" }, - { type: "AgreementArchivedByConsumer" }, - { type: "AgreementArchivedByUpgrade" }, - { type: "AgreementUpgraded" }, { type: "AgreementSuspendedByProducer" }, { type: "AgreementSuspendedByConsumer" }, { type: "AgreementSuspendedByPlatform" }, + async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + if (!agreementEntry || agreementEntry.version > msg.version) { + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version + ); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + }); + } + } + } + ) + .with({ type: "AgreementUpgraded" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (agreementEntry) { + if (agreementEntry.version > msg.version) { + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version + ); + } + } else { + if (!agreement.stamps.activation) { + throw genericInternalError( + "An activated agreement should have activation stamp" + ); + } + const newAgreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msg.version, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + agreement.stamps.activation.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(newAgreementEntry, dynamoDBClient); + } + + const updateLatestAgreementOnTokenStates = async ( + catalogEntry: PlatformStatesCatalogEntry | undefined + ): Promise => { + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId( + { + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + } + ); + + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } + }; + + const catalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + + await updateLatestAgreementOnTokenStates(catalogEntry); + + const updatedCatalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + + if ( + updatedCatalogEntry && + (!catalogEntry || updatedCatalogEntry.state !== catalogEntry.state) + ) { + await updateLatestAgreementOnTokenStates(updatedCatalogEntry); + } + }) + .with({ type: "AgreementArchivedByUpgrade" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + const pk = makePlatformStatesAgreementPK(agreement.id); + await deleteAgreementEntry(pk, dynamoDBClient); + }) + .with({ type: "AgreementArchivedByConsumer" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + }); + } + + await deleteAgreementEntry(primaryKey, dynamoDBClient); + }) + .with( + { type: "AgreementAdded" }, + { type: "AgreementDeleted" }, + { type: "DraftAgreementUpdated" }, + { type: "AgreementSubmitted" }, { type: "AgreementRejected" }, { type: "AgreementConsumerDocumentAdded" }, { type: "AgreementConsumerDocumentRemoved" }, @@ -31,3 +296,11 @@ export async function handleMessageV2( ) .exhaustive(); } + +const parseAgreement = (agreementV2: AgreementV2 | undefined): Agreement => { + if (!agreementV2) { + throw genericInternalError(`Agreement not found in message data`); + } + + return fromAgreementV2(agreementV2); +}; diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index 678ef75f8a..b42a26d449 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -1,9 +1,20 @@ import { + AgreementId, + agreementState, + AgreementState, genericInternalError, + GSIPKConsumerIdEServiceId, + GSIPKEServiceIdDescriptorId, + itemState, + ItemState, PlatformStatesAgreementEntry, PlatformStatesAgreementPK, + PlatformStatesCatalogEntry, + PlatformStatesEServiceDescriptorPK, + TokenGenerationStatesClientPurposeEntry, } from "pagopa-interop-models"; import { + AttributeValue, DeleteItemCommand, DeleteItemInput, DynamoDBClient, @@ -12,8 +23,14 @@ import { GetItemInput, PutItemCommand, PutItemInput, + QueryCommand, + QueryCommandOutput, + QueryInput, + UpdateItemCommand, + UpdateItemInput, } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { z } from "zod"; import { config } from "./config/config.js"; export const writeAgreementEntry = async ( @@ -21,6 +38,7 @@ export const writeAgreementEntry = async ( dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", Item: { PK: { S: agreementEntry.PK, @@ -37,7 +55,7 @@ export const writeAgreementEntry = async ( GSIPK_consumerId_eserviceId: { S: agreementEntry.GSIPK_consumerId_eserviceId, }, - GSI_agreementTimestamp: { + GSISK_agreementTimestamp: { S: agreementEntry.GSISK_agreementTimestamp, }, agreementDescriptorId: { @@ -93,3 +111,431 @@ export const deleteAgreementEntry = async ( const command = new DeleteItemCommand(input); await dynamoDBClient.send(command); }; + +export const updateAgreementStateInPlatformStatesEntry = async ( + dynamoDBClient: DynamoDBClient, + primaryKey: PlatformStatesAgreementPK, + state: ItemState, + version: number +): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: state, + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + ExpressionAttributeNames: { + "#state": "state", + }, + UpdateExpression: + "SET #state = :newState, version = :newVersion, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const agreementStateToItemState = (state: AgreementState): ItemState => + state === agreementState.active ? itemState.active : itemState.inactive; + +export const updateAgreementStateOnTokenStatesEntries = async ({ + entriesToUpdate, + agreementState, + dynamoDBClient, +}: { + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; +}): Promise => { + for (const entry of entriesToUpdate) { + const input: UpdateItemInput = { + // ConditionExpression to avoid upsert + ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: agreementStateToItemState(agreementState), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET agreementState = :newState, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; + +export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = + async ({ + entriesToUpdate, + agreementState, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }: { + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; + GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; + catalogEntry: PlatformStatesCatalogEntry | undefined; + }): Promise => { + for (const entry of entriesToUpdate) { + const additionalDescriptorInfo = + catalogEntry && + (!entry.GSIPK_eserviceId_descriptorId || !entry.descriptorState); + + const additionalAttributesToSet: Record = + additionalDescriptorInfo + ? { + ":descriptorState": { + S: catalogEntry.state, + }, + ":descriptorAudience": { + L: catalogEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + ":descriptorVoucherLifespan": { + N: catalogEntry.descriptorVoucherLifespan.toString(), + }, + ":gsi": { + S: GSIPK_eserviceId_descriptorId, + }, + } + : {}; + const input: UpdateItemInput = { + // ConditionExpression to avoid upsert + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: agreementStateToItemState(agreementState), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + ...additionalAttributesToSet, + }, + UpdateExpression: + "SET agreementState = :newState, updatedAt = :newUpdateAt".concat( + additionalDescriptorInfo + ? ", GSI_eservice_id_descriptor_id = :gsi, descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan" + : "" + ), + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } + }; + +export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNamePlatform, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + ScanIndexForward: false, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform state agreement entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const agreementEntries = z + .array(PlatformStatesAgreementEntry) + .safeParse(unmarshalledItems); + + if (!agreementEntries.success) { + throw genericInternalError( + `Unable to parse platform state entry items: result ${JSON.stringify( + agreementEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return agreementEntries.data; + } else { + return [ + ...agreementEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ + GSIPK_consumerId_eserviceId, + agreementState, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, +}: { + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; + GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; + catalogEntry: PlatformStatesCatalogEntry | undefined; +}): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateAgreementStateAndDescriptorInfoOnTokenStatesEntries({ + entriesToUpdate: tokenStateEntries.data, + agreementState, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + GSIPK_consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const extractAgreementIdFromAgreementPK = ( + pk: PlatformStatesAgreementPK +): AgreementId => { + const substrings = pk.split("#"); + const agreementId = substrings[1]; + const result = AgreementId.safeParse(agreementId); + + if (!result.success) { + throw genericInternalError( + `Unable to parse agreement PK: result ${JSON.stringify( + result + )} - data ${JSON.stringify(agreementId)} ` + ); + } + return result.data; +}; + +export const updateAgreementStateOnTokenStates = async ({ + GSIPK_consumerId_eserviceId, + agreementState, + dynamoDBClient, +}: { + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; + agreementState: AgreementState; + dynamoDBClient: DynamoDBClient; +}): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateAgreementStateOnTokenStatesEntries({ + entriesToUpdate: tokenStateEntries.data, + agreementState, + dynamoDBClient, + }); + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + GSIPK_consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const readCatalogEntry = async ( + primaryKey: PlatformStatesEServiceDescriptorPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const catalogEntry = PlatformStatesCatalogEntry.safeParse(unmarshalled); + + if (!catalogEntry.success) { + throw genericInternalError( + `Unable to parse catalog entry item: result ${JSON.stringify( + catalogEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return catalogEntry.data; + } +}; + +export const isLatestAgreement = async ( + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId, + agreementId: AgreementId, + dynamoDBClient: DynamoDBClient +): Promise => { + const agreementEntries = + await readPlatformStateAgreementEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + if (agreementEntries.length === 0) { + return true; + } + const agreementIdFromEntry = extractAgreementIdFromAgreementPK( + agreementEntries[0].PK + ); + return agreementIdFromEntry === agreementId; +}; diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts new file mode 100644 index 0000000000..f1cdca56f4 --- /dev/null +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -0,0 +1,3137 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + Agreement, + AgreementActivatedV2, + AgreementArchivedByConsumerV2, + AgreementArchivedByUpgradeV2, + AgreementEventEnvelope, + AgreementSuspendedByConsumerV2, + AgreementSuspendedByPlatformV2, + AgreementSuspendedByProducerV2, + AgreementUnsuspendedByConsumerV2, + AgreementUnsuspendedByProducerV2, + AgreementUpgradedV2, + EServiceId, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + TenantId, + TokenGenerationStatesClientPurposeEntry, + agreementState, + generateId, + itemState, + makeGSIPKConsumerIdEServiceId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + makeTokenGenerationStatesClientKidPurposePK, + toAgreementV2, +} from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + getMockTokenStatesClientPurposeEntry, + getMockAgreement, + buildDynamoDBTables, + deleteDynamoDBTables, + readTokenStateEntriesByConsumerIdEserviceId, + writeTokenStateEntry, + getMockAgreementEntry, + writeCatalogEntry, +} from "pagopa-interop-commons-test"; +import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; +import { handleMessageV2 } from "../src/consumerServiceV2.js"; +import { config, sleep } from "./utils.js"; + +describe("integration tests V2 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("AgreementActivated", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than existing table entry", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 3, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousStateEntry, + state: itemState.active, + version: 3, + updatedAt: new Date().toISOString(), + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist - and add descriptor info to token-generation-states entry if missing", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + + it("should add the entry if it doesn't exist (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: previousAgreement.consumerId, + eserviceId: previousAgreement.eserviceId, + }), + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: previousAgreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + describe("AgreementSuspendedByProducer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByProducerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementSuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByProducerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementSuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByProducerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementSuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + describe("AgreementSuspendedByConsumer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByConsumerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementSuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByConsumerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementSuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByConsumerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementSuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + describe("AgreementSuspendedByPlatform", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByPlatformV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementSuspendedByPlatform", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByPlatformV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementSuspendedByPlatform", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementSuspendedByPlatformV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementSuspendedByPlatform", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + describe("AgreementUnsuspendedByProducer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByProducerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUnsuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByProducerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUnsuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByProducerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUnsuspendedByProducer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("AgreementUnsuspendedByConsumer", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByConsumerV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUnsuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByConsumerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUnsuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUnsuspendedByConsumerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUnsuspendedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("AgreementUpgraded", async () => { + it("should do no operation if the table entry is more recent than incoming version", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 3, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: latestAgreement.eserviceId, + descriptorId: latestAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is not the latest -> no operation in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + + describe("AgreementArchivedByUpgrade", () => { + it("should delete the entry (no update in token-generation-states)", async () => { + const consumerId = generateId(); + const eserviceId = generateId(); + + const agreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + archiving: { + when: new Date(), + who: generateId(), + }, + }, + }; + + const payload: AgreementArchivedByUpgradeV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementArchivedByUpgrade", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + }; + + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + + describe("AgreementArchivedByConsumer", () => { + it("should delete the entry if it exists (agreement is the latest -> includes operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementArchivedByConsumerV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementArchivedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should delete the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementArchivedByConsumerV2 = { + agreement: toAgreementV2(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementArchivedByConsumer", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + }); +}); diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts new file mode 100644 index 0000000000..67b950fc6d --- /dev/null +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -0,0 +1,650 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import crypto from "crypto"; +import { + ConditionalCheckFailedException, + DynamoDBClient, +} from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockAgreementEntry, + getMockTokenStatesClientPurposeEntry, + readAllTokenStateItems, + readTokenStateEntriesByConsumerIdEserviceId, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { + makePlatformStatesAgreementPK, + generateId, + AgreementId, + itemState, + PlatformStatesAgreementEntry, + makeGSIPKConsumerIdEServiceId, + DescriptorId, + makePlatformStatesEServiceDescriptorPK, + agreementState, + makeTokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesClientPurposeEntry, + makeGSIPKEServiceIdDescriptorId, + EServiceId, + PlatformStatesCatalogEntry, + TenantId, +} from "pagopa-interop-models"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + updateAgreementStateInPlatformStatesEntry, + readAgreementEntry, + writeAgreementEntry, + deleteAgreementEntry, + agreementStateToItemState, + updateAgreementStateOnTokenStates, + updateAgreementStateAndDescriptorInfoOnTokenStates, + isLatestAgreement, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("utils", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("updateAgreementStateInPlatformStatesEntry", async () => { + it("should throw error if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + expect( + updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + itemState.active, + 1 + ) + ).rejects.toThrowError(ConditionalCheckFailedException); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(agreementEntry).toBeUndefined(); + }); + + it("should update state if previous entry exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const previousAgreementStateEntry = getMockAgreementEntry(primaryKey); + expect( + await readAgreementEntry(primaryKey, dynamoDBClient) + ).toBeUndefined(); + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + itemState.active, + 2 + ); + + const result = await readAgreementEntry(primaryKey, dynamoDBClient); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + + expect(result).toEqual(expectedAgreementEntry); + }); + }); + + describe("writeAgreementEntry", async () => { + it("should throw error if previous entry exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const agreementStateEntry = getMockAgreementEntry(primaryKey); + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + expect( + writeAgreementEntry(agreementStateEntry, dynamoDBClient) + ).rejects.toThrowError(ConditionalCheckFailedException); + }); + + it("should write if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toEqual(agreementStateEntry); + }); + }); + + describe("readAgreementEntry", async () => { + it("should return undefined if entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(agreementEntry).toBeUndefined(); + }); + + it("should return entry if it exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + const retrievedEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + + expect(retrievedEntry).toEqual(agreementStateEntry); + }); + }); + + describe("deleteAgreementEntry", async () => { + it("should not throw error if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + expect( + deleteAgreementEntry(primaryKey, dynamoDBClient) + ).resolves.not.toThrowError(); + }); + + it("should delete the entry if it exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + await deleteAgreementEntry(primaryKey, dynamoDBClient); + const retrievedAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toBeUndefined(); + }); + }); + + describe("agreementStateToItemState", async () => { + it.each([agreementState.active])( + "should convert %s state to active", + async (s) => { + expect(agreementStateToItemState(s)).toBe(itemState.active); + } + ); + + it.each([agreementState.archived, agreementState.suspended])( + "should convert %s state to inactive", + async (s) => { + expect(agreementStateToItemState(s)).toBe(itemState.inactive); + } + ); + }); + + describe("readTokenStateEntriesByConsumerIdEserviceId", async () => { + it("should return empty array if entries do not exist", async () => { + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const result = await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + expect(result).toEqual([]); + }); + + it("should return entries if they exist (no need for pagination)", async () => { + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const tokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(tokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(tokenStateEntry2, dynamoDBClient); + + const retrievedTokenEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenStateEntry1, tokenStateEntry2]) + ); + }); + + it("should return entries if they exist (with pagination)", async () => { + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + + const tokenEntriesLength = 10; + + const writtenEntries = []; + // eslint-disable-next-line functional/no-let + for (let i = 0; i < tokenEntriesLength; i++) { + const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + // eslint-disable-next-line functional/immutable-data + writtenEntries.push(tokenStateEntry); + } + vi.spyOn(dynamoDBClient, "send"); + const tokenEntries = await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); + expect(tokenEntries).toHaveLength(tokenEntriesLength); + expect(tokenEntries).toEqual(expect.arrayContaining(writtenEntries)); + }); + }); + + describe("updateAgreementStateOnTokenStates", async () => { + it("should do nothing if previous entry doesn't exist", async () => { + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); + expect(tokenStateEntries).toEqual([]); + expect( + updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.archived, + dynamoDBClient, + }) + ).resolves.not.toThrowError(); + const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + dynamoDBClient + ); + expect(tokenStateEntriesAfterUpdate).toEqual([]); + }); + + it("should update state if previous entries exist", async () => { + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.active, + dynamoDBClient, + }); + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + }); + + describe("updateAgreementStateAndDescriptorInfoOnTokenStates", async () => { + it("should do nothing if previous entry doesn't exist", async () => { + const eserviceId = generateId(); + const descriptorId = generateId(); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId, + }); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: generateId(), + }); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId, + descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: pkCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + version: 3, + updatedAt: new Date().toISOString(), + }; + + const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); + expect(tokenStateEntries).toEqual([]); + expect( + updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.archived, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }) + ).resolves.not.toThrowError(); + const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + dynamoDBClient + ); + expect(tokenStateEntriesAfterUpdate).toEqual([]); + }); + + it("should update state if previous entries exist", async () => { + const eserviceId = generateId(); + const descriptorId = generateId(); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId, + }); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: generateId(), + }); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId, + descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: pkCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + version: 3, + updatedAt: new Date().toISOString(), + }; + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + descriptorState: undefined, + descriptorAudience: [], + descriptorVoucherLifespan: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + descriptorState: undefined, + descriptorAudience: [], + descriptorVoucherLifespan: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreementState.active, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("isLatestAgreement", () => { + it("should return true if the agreement is the latest", async () => { + const eserviceId = generateId(); + const consumerId = generateId(); + const agreementId1 = generateId(); + const agreementId2 = generateId(); + const now = new Date(); + const threeHoursAgo = new Date(); + threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const agreementPK1 = makePlatformStatesAgreementPK(agreementId1); + const agreementPK2 = makePlatformStatesAgreementPK(agreementId2); + + const agreementEntry1: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementPK1, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: now.toISOString(), + state: itemState.active, + }; + + const agreementEntry2: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementPK2, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: threeHoursAgo.toISOString(), + state: itemState.inactive, + }; + + await writeAgreementEntry(agreementEntry1, dynamoDBClient); + await writeAgreementEntry(agreementEntry2, dynamoDBClient); + + expect( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreementId1, + dynamoDBClient + ) + ).toEqual(true); + + expect( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreementId2, + dynamoDBClient + ) + ).toEqual(false); + }); + + it("should return true if there are no other agreements", async () => { + const eserviceId = generateId(); + const consumerId = generateId(); + const agreementId1 = generateId(); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + expect( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreementId1, + dynamoDBClient + ) + ).toEqual(true); + }); + }); +}); diff --git a/packages/agreement-platformstate-writer/test/utils.ts b/packages/agreement-platformstate-writer/test/utils.ts new file mode 100644 index 0000000000..fdc2e3c2c5 --- /dev/null +++ b/packages/agreement-platformstate-writer/test/utils.ts @@ -0,0 +1,11 @@ +import { inject, vi } from "vitest"; + +export const config = inject("tokenGenerationReadModelConfig"); + +export const sleep = (ms: number, mockDate = new Date()): Promise => + new Promise((resolve) => { + vi.useRealTimers(); + setTimeout(resolve, ms); + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index ed13803846..2caf1da813 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -48,8 +48,6 @@ describe("utils tests", async () => { fail(); } const dynamoDBClient = new DynamoDBClient({ - credentials: { accessKeyId: "key", secretAccessKey: "secret" }, - region: "eu-south-1", endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, }); beforeEach(async () => { diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 63d4569838..15a439945e 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -48,6 +48,9 @@ import { PurposeVersionId, ProducerKeychain, DescriptorState, + GSIPKConsumerIdEServiceId, + PlatformStatesAgreementEntry, + PlatformStatesAgreementPK, } from "pagopa-interop-models"; import { AuthData } from "pagopa-interop-commons"; import { z } from "zod"; @@ -368,3 +371,21 @@ export const getMockTokenStatesClientPurposeEntry = ( }), }; }; + +export const getMockAgreementEntry = ( + primaryKey: PlatformStatesAgreementPK, + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId( + { + consumerId: generateId(), + eserviceId: generateId(), + } + ) +): PlatformStatesAgreementEntry => ({ + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), +}); diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 4535e4b974..36a78b5c87 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -14,7 +14,9 @@ import { } from "@aws-sdk/client-dynamodb"; import { genericInternalError, + GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, + PlatformStatesCatalogEntry, TokenGenerationStatesClientPurposeEntry, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; @@ -24,80 +26,110 @@ export const writeTokenStateEntry = async ( tokenStateEntry: TokenGenerationStatesClientPurposeEntry, dynamoDBClient: DynamoDBClient ): Promise => { - const agreementItems: Record = - tokenStateEntry.GSIPK_consumerId_eserviceId - ? { - agreementId: { - S: tokenStateEntry.agreementId!, - }, - agreementState: { - S: tokenStateEntry.agreementState!, - }, - GSIPK_consumerId_eserviceId: { - S: tokenStateEntry.GSIPK_consumerId_eserviceId, - }, - } - : {}; - const descriptorItems: Record = - tokenStateEntry.GSIPK_eserviceId_descriptorId - ? { - descriptorState: { - S: tokenStateEntry.descriptorState!, - }, - descriptorAudience: { - L: tokenStateEntry.descriptorAudience!.map((item) => ({ - S: item, - })), - }, - descriptorVoucherLifespan: { - N: tokenStateEntry.descriptorVoucherLifespan!.toString(), - }, - GSIPK_eserviceId_descriptorId: { - S: tokenStateEntry.GSIPK_eserviceId_descriptorId, - }, - } - : {}; - const items: Record = { - ...agreementItems, - ...descriptorItems, - PK: { - S: tokenStateEntry.PK, - }, - updatedAt: { - S: tokenStateEntry.updatedAt, - }, - consumerId: { - S: tokenStateEntry.consumerId, - }, - purposeVersionId: { - S: tokenStateEntry.purposeVersionId!, - }, - clientKind: { - S: tokenStateEntry.clientKind, - }, - publicKey: { - S: tokenStateEntry.publicKey, - }, - GSIPK_clientId: { - S: tokenStateEntry.GSIPK_clientId, - }, - GSIPK_kid: { - S: tokenStateEntry.GSIPK_kid, - }, - GSIPK_clientId_purposeId: { - S: tokenStateEntry.GSIPK_clientId_purposeId!, - }, - GSIPK_purposeId: { - S: tokenStateEntry.GSIPK_purposeId!, - }, - purposeState: { - S: tokenStateEntry.purposeState!, - }, - }; - const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", - Item: items, + Item: { + PK: { + S: tokenStateEntry.PK, + }, + ...(tokenStateEntry.descriptorState + ? { + descriptorState: { + S: tokenStateEntry.descriptorState, + }, + } + : {}), + ...(tokenStateEntry.descriptorAudience + ? { + descriptorAudience: { + L: tokenStateEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + } + : {}), + ...(tokenStateEntry.descriptorVoucherLifespan + ? { + descriptorVoucherLifespan: { + N: tokenStateEntry.descriptorVoucherLifespan.toString(), + }, + } + : {}), + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + ...(tokenStateEntry.agreementId + ? { + agreementId: { + S: tokenStateEntry.agreementId, + }, + } + : {}), + ...(tokenStateEntry.purposeVersionId + ? { + purposeVersionId: { + S: tokenStateEntry.purposeVersionId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_consumerId_eserviceId + ? { + GSIPK_consumerId_eserviceId: { + S: tokenStateEntry.GSIPK_consumerId_eserviceId, + }, + } + : {}), + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + ...(tokenStateEntry.GSIPK_clientId_purposeId + ? { + GSIPK_clientId_purposeId: { + S: tokenStateEntry.GSIPK_clientId_purposeId, + }, + } + : {}), + ...(tokenStateEntry.agreementState + ? { + agreementState: { + S: tokenStateEntry.agreementState, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_eserviceId_descriptorId + ? { + GSIPK_eserviceId_descriptorId: { + S: tokenStateEntry.GSIPK_eserviceId_descriptorId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_purposeId + ? { + GSIPK_purposeId: { + S: tokenStateEntry.GSIPK_purposeId, + }, + } + : {}), + ...(tokenStateEntry.purposeState + ? { + purposeState: { + S: tokenStateEntry.purposeState, + }, + } + : {}), + }, TableName: "token-generation-states", }; const command = new PutItemCommand(input); @@ -196,3 +228,99 @@ export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( undefined ); }; + +export const readTokenStateEntriesByConsumerIdEserviceId = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + consumerId_eserviceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: "token-generation-states", + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: consumerId_eserviceId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + consumerId_eserviceId, + dynamoDBClient, + undefined + ); +}; + +export const writeCatalogEntry = async ( + catalogEntry: PlatformStatesCatalogEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: catalogEntry.PK, + }, + state: { + S: catalogEntry.state, + }, + descriptorAudience: { + L: catalogEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + descriptorVoucherLifespan: { + N: catalogEntry.descriptorVoucherLifespan.toString(), + }, + version: { + N: catalogEntry.version.toString(), + }, + updatedAt: { + S: catalogEntry.updatedAt, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; From 5bd798977b05b1242bfad19a315c5cda89c99854 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 31 Oct 2024 15:28:21 +0100 Subject: [PATCH 004/126] IMN-791 Add events service v1 in agreement-platformstate-writer (#1014) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 347 ++- ...entPlatformstateWriter.integration.test.ts | 7 - .../consumerServiceV1.integration.test.ts | 2084 +++++++++++++++++ 3 files changed, 2425 insertions(+), 13 deletions(-) delete mode 100644 packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts create mode 100644 packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 8160ce4de1..3e3254bc4b 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -1,20 +1,99 @@ import { match } from "ts-pattern"; -import { AgreementEventEnvelopeV1 } from "pagopa-interop-models"; +import { + Agreement, + AgreementEventEnvelopeV1, + AgreementV1, + genericInternalError, + fromAgreementV1, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + PlatformStatesAgreementEntry, + agreementState, + PlatformStatesCatalogEntry, +} from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + readAgreementEntry, + updateAgreementStateInPlatformStatesEntry, + agreementStateToItemState, + updateAgreementStateOnTokenStates, + writeAgreementEntry, + readCatalogEntry, + updateAgreementStateAndDescriptorInfoOnTokenStates, + deleteAgreementEntry, + isLatestAgreement, +} from "./utils.js"; export async function handleMessageV1( message: AgreementEventEnvelopeV1, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "AgreementActivated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + await handleFirstActivation(agreement, dynamoDBClient, msg.version); + }) + .with({ type: "AgreementSuspended" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + await handleActivationOrSuspension( + agreement, + dynamoDBClient, + msg.version + ); + }) + .with({ type: "AgreementUpdated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + + await match(agreement.state) + // eslint-disable-next-line sonarjs/no-identical-functions + .with(agreementState.active, agreementState.suspended, async () => { + const agreement = parseAgreement(msg.data.agreement); + await handleActivationOrSuspension( + agreement, + dynamoDBClient, + msg.version + ); + }) + .with(agreementState.archived, async () => { + const agreement = parseAgreement(msg.data.agreement); + await handleArchiving(agreement, dynamoDBClient); + }) + .with( + agreementState.draft, + agreementState.missingCertifiedAttributes, + agreementState.pending, + agreementState.rejected, + () => Promise.resolve() + ) + .exhaustive(); + }) + .with({ type: "AgreementAdded" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + + await match(agreement.state) + // eslint-disable-next-line sonarjs/no-identical-functions + .with(agreementState.active, async () => { + // this case is for agreement upgraded + const agreement = parseAgreement(msg.data.agreement); + await handleUpgrade(agreement, dynamoDBClient, msg.version); + }) + .with( + agreementState.draft, + agreementState.archived, + agreementState.missingCertifiedAttributes, + agreementState.pending, + agreementState.rejected, + agreementState.suspended, + () => Promise.resolve() + ) + .exhaustive(); + }) .with( - { type: "AgreementAdded" }, - { type: "AgreementActivated" }, - { type: "AgreementSuspended" }, { type: "AgreementDeactivated" }, { type: "AgreementDeleted" }, { type: "VerifiedAttributeUpdated" }, - { type: "AgreementUpdated" }, { type: "AgreementConsumerDocumentAdded" }, { type: "AgreementConsumerDocumentRemoved" }, { type: "AgreementContractAdded" }, @@ -22,3 +101,259 @@ export async function handleMessageV1( ) .exhaustive(); } + +const parseAgreement = (agreementV1: AgreementV1 | undefined): Agreement => { + if (!agreementV1) { + throw genericInternalError(`Agreement not found in message data`); + } + + return fromAgreementV1(agreementV1); +}; + +const handleFirstActivation = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient, + incomingVersion: number +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + + const existingAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (existingAgreementEntry) { + if (existingAgreementEntry.version > incomingVersion) { + // Stops processing if the message is older than the agreement entry + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + incomingVersion + ); + } + } else { + const agreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: incomingVersion, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(agreementEntry, dynamoDBClient); + } + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + // token-generation-states + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } +}; + +const handleActivationOrSuspension = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient, + incomingVersion: number +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + + const existingAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (existingAgreementEntry) { + if (existingAgreementEntry.version > incomingVersion) { + // Stops processing if the message is older than the agreement entry + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + incomingVersion + ); + } + } + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); + + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } +}; + +const handleArchiving = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + + await updateAgreementStateOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + }); + } + + await deleteAgreementEntry(primaryKey, dynamoDBClient); +}; + +const handleUpgrade = async ( + agreement: Agreement, + dynamoDBClient: DynamoDBClient, + msgVersion: number +): Promise => { + const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); + + const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + if (agreementEntry) { + if (agreementEntry.version > msgVersion) { + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msgVersion + ); + } + } else { + const newAgreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msgVersion, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + await writeAgreementEntry(newAgreementEntry, dynamoDBClient); + } + + const updateLatestAgreementOnTokenStates = async ( + catalogEntry: PlatformStatesCatalogEntry | undefined + ): Promise => { + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + await updateAgreementStateAndDescriptorInfoOnTokenStates({ + GSIPK_consumerId_eserviceId, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry, + }); + } + }; + + await updateLatestAgreementOnTokenStates(catalogEntry); + + const secondRetrievalCatalogEntry = await readCatalogEntry( + pkCatalogEntry, + dynamoDBClient + ); + if ( + secondRetrievalCatalogEntry && + (!catalogEntry || secondRetrievalCatalogEntry.state !== catalogEntry.state) + ) { + await updateLatestAgreementOnTokenStates(secondRetrievalCatalogEntry); + } +}; diff --git a/packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts b/packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts deleted file mode 100644 index e110c51648..0000000000 --- a/packages/agreement-platformstate-writer/test/agreementPlatformstateWriter.integration.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { describe, expect, it } from "vitest"; - -describe("sample", () => { - it("test", () => { - expect(1).toBe(1); - }); -}); diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts new file mode 100644 index 0000000000..339672bc55 --- /dev/null +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -0,0 +1,2084 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + Agreement, + AgreementActivatedV1, + AgreementAddedV1, + AgreementEventEnvelope, + AgreementUpdatedV1, + EServiceId, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + TenantId, + TokenGenerationStatesClientPurposeEntry, + agreementState, + generateId, + itemState, + makeGSIPKConsumerIdEServiceId, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + makeTokenGenerationStatesClientKidPurposePK, +} from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + getMockTokenStatesClientPurposeEntry, + getMockAgreement, + buildDynamoDBTables, + deleteDynamoDBTables, + toAgreementV1, + readTokenStateEntriesByConsumerIdEserviceId, + getMockAgreementEntry, + writeCatalogEntry, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; +import { handleMessageV1 } from "../src/consumerServiceV1.js"; +import { config, sleep } from "./utils.js"; + +describe("integration tests V1 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("AgreementActivated", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than existing table entry", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 3, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 2, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousStateEntry, + state: itemState.active, + version: 3, + updatedAt: new Date().toISOString(), + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist - and add descriptor info to token-generation-states entry if missing", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should add the entry if it doesn't exist (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + descriptorAudience: undefined, + descriptorState: undefined, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: previousAgreement.consumerId, + eserviceId: previousAgreement.eserviceId, + }), + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: previousAgreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + describe("AgreementAdded (upgrade)", async () => { + it("should do no operation if the table entry is more recent than incoming version", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementEntryPrimaryKey), + version: 3, + }; + await writeAgreementEntry(previousStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: latestAgreement.eserviceId, + descriptorId: latestAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is not the latest -> no operation in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + version: 1, + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: previousAgreement.eserviceId, + descriptorId: previousAgreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + it("add the entry if it doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCatalogEntry, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + version: 1, + state: itemState.active, + updatedAt: agreement.stamps.activation!.when.toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); + + describe("AgreementUpdated (suspended by producer)", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.suspended, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.active, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("AgreementUpdated (unsuspended by producer)", async () => { + it("should do no operation if the entry doesn't exist", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( + agreement.id + ); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + + expect(retrievedAgreementEntry).toBeUndefined(); + }); + it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry (agreement is the latest -> update in token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + ...latestAgreementStateEntry, + state: itemState.active, + updatedAt: new Date().toISOString(), + }; + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + }); + + describe("Agreement Updated (archived by consumer or by upgrade)", () => { + it("agreement is the latest (includes operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + archiving: { + when: new Date(), + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry1, + expectedTokenStateEntry2, + ]) + ); + }); + it("agreement is not the latest (no operation on token states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpdatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementUpdated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await sleep(1000, mockDate); + await handleMessageV1(message, dynamoDBClient); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByConsumerIdEserviceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + }); +}); From 59a572d7c7ba1c16c2ec2dc9bc1700236e0449d4 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 5 Nov 2024 14:38:31 +0100 Subject: [PATCH 005/126] IMN-784 - Fixing attribute duplicate errors (#1136) --- .../src/model/domain/errors.ts | 15 +++++- .../src/services/attributeRegistryService.ts | 17 ++++-- ...tributeRegistryService.integration.test.ts | 53 +++++++++++++------ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/packages/attribute-registry-process/src/model/domain/errors.ts b/packages/attribute-registry-process/src/model/domain/errors.ts index 7972d7f8ed..16eb6e43cd 100644 --- a/packages/attribute-registry-process/src/model/domain/errors.ts +++ b/packages/attribute-registry-process/src/model/domain/errors.ts @@ -24,16 +24,27 @@ export function attributeNotFound(identifier: string): ApiError { }); } -export function attributeDuplicate( +export function attributeDuplicateByName( attributeName: string ): ApiError { return new ApiError({ - detail: `ApiError during Attribute creation with name ${attributeName}`, + detail: `An attribute with name ${attributeName} already exists`, code: "attributeDuplicate", title: "Duplicated attribute name", }); } +export function attributeDuplicateByNameAndCode( + attributeName: string, + attributeCode: string +): ApiError { + return new ApiError({ + detail: `An attribute with name ${attributeName} and code ${attributeCode} already exists`, + code: "attributeDuplicate", + title: "Duplicated attribute name and code", + }); +} + export function originNotCompliant(origin: string): ApiError { return new ApiError({ detail: `Requester origin ${origin} is not allowed`, diff --git a/packages/attribute-registry-process/src/services/attributeRegistryService.ts b/packages/attribute-registry-process/src/services/attributeRegistryService.ts index 57f20ab31d..4c4b9a37f0 100644 --- a/packages/attribute-registry-process/src/services/attributeRegistryService.ts +++ b/packages/attribute-registry-process/src/services/attributeRegistryService.ts @@ -20,7 +20,8 @@ import { attributeRegistryApi } from "pagopa-interop-api-clients"; import { toCreateEventAttributeAdded } from "../model/domain/toEvent.js"; import { OrganizationIsNotACertifier, - attributeDuplicate, + attributeDuplicateByName, + attributeDuplicateByNameAndCode, attributeNotFound, originNotCompliant, tenantNotFound, @@ -141,7 +142,7 @@ export function attributeRegistryServiceBuilder( apiDeclaredAttributeSeed.name ); if (attributeWithSameName) { - throw attributeDuplicate(apiDeclaredAttributeSeed.name); + throw attributeDuplicateByName(apiDeclaredAttributeSeed.name); } const newDeclaredAttribute: Attribute = { @@ -182,7 +183,7 @@ export function attributeRegistryServiceBuilder( apiVerifiedAttributeSeed.name ); if (attributeWithSameName) { - throw attributeDuplicate(apiVerifiedAttributeSeed.name); + throw attributeDuplicateByName(apiVerifiedAttributeSeed.name); } const newVerifiedAttribute: Attribute = { @@ -230,7 +231,10 @@ export function attributeRegistryServiceBuilder( ]); if (attributeWithSameName) { - throw attributeDuplicate(apiCertifiedAttributeSeed.name); + throw attributeDuplicateByNameAndCode( + apiCertifiedAttributeSeed.name, + apiCertifiedAttributeSeed.code + ); } const newCertifiedAttribute: Attribute = { @@ -270,7 +274,10 @@ export function attributeRegistryServiceBuilder( apiInternalCertifiedAttributeSeed.name ); if (attributeWithSameNameAndCode) { - throw attributeDuplicate(apiInternalCertifiedAttributeSeed.name); + throw attributeDuplicateByNameAndCode( + apiInternalCertifiedAttributeSeed.name, + apiInternalCertifiedAttributeSeed.code + ); } const newInternalCertifiedAttribute: Attribute = { diff --git a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts index 1064f5ddd2..b33fee3535 100644 --- a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts +++ b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts @@ -18,7 +18,8 @@ import { } from "pagopa-interop-models"; import { OrganizationIsNotACertifier, - attributeDuplicate, + attributeDuplicateByName, + attributeDuplicateByNameAndCode, originNotCompliant, tenantNotFound, } from "../src/model/domain/errors.js"; @@ -99,16 +100,17 @@ describe("database test", () => { ) ).rejects.toThrowError(originNotCompliant("not-allowed-origin")); }); - it("should throw attributeDuplicate if an attribute with the same name already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name already exists, case insensitive", async () => { const attribute = { ...mockAttribute, + name: mockAttribute.name.toUpperCase(), kind: attributeKind.declared, }; await addOneAttribute(attribute); expect( attributeRegistryService.createDeclaredAttribute( { - name: attribute.name, + name: attribute.name.toLowerCase(), description: attribute.description, }, { @@ -118,7 +120,9 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByName(attribute.name.toLowerCase()) + ); }); }); describe("verified attribute creation", () => { @@ -185,16 +189,17 @@ describe("database test", () => { ) ).rejects.toThrowError(originNotCompliant("not-allowed-origin")); }); - it("should throw attributeDuplicate if an attribute with the same name already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name already exists, case insensitive", async () => { const attribute = { ...mockAttribute, + name: mockAttribute.name.toUpperCase(), kind: attributeKind.verified, }; await addOneAttribute(attribute); expect( attributeRegistryService.createVerifiedAttribute( { - name: attribute.name, + name: attribute.name.toLowerCase(), description: attribute.description, }, { @@ -204,7 +209,9 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByName(attribute.name.toLowerCase()) + ); }); }); describe("certified attribute creation", () => { @@ -263,10 +270,11 @@ describe("database test", () => { ); expect(writtenPayload.attribute).toEqual(toAttributeV1(attribute)); }); - it("should throw attributeDuplicate if an attribute with the same name and code already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name and code already exists, case insensitive", async () => { const attribute = { ...mockAttribute, - code: "123456", + name: mockAttribute.name.toUpperCase(), + code: "123456AB", }; const tenant: Tenant = { @@ -284,8 +292,8 @@ describe("database test", () => { expect( attributeRegistryService.createCertifiedAttribute( { - name: attribute.name, - code: attribute.code, + name: attribute.name.toLowerCase(), + code: attribute.code.toLowerCase(), description: attribute.description, }, { @@ -295,7 +303,12 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByNameAndCode( + attribute.name.toLowerCase(), + attribute.code.toLowerCase() + ) + ); }); it("should throw OrganizationIsNotACertifier if the organization is not a certifier", async () => { await addOneTenant(mockTenant); @@ -391,10 +404,11 @@ describe("database test", () => { ); expect(writtenPayload.attribute).toEqual(toAttributeV1(attribute)); }); - it("should throw attributeDuplicate if an attribute with the same name and code already exists", async () => { + it("should throw attributeDuplicate if an attribute with the same name and code already exists, case insensitive", async () => { const attribute = { ...mockAttribute, - code: "123456", + name: mockAttribute.name.toUpperCase(), + code: "123456AB", }; const tenant: Tenant = { @@ -412,8 +426,8 @@ describe("database test", () => { expect( attributeRegistryService.createInternalCertifiedAttribute( { - name: attribute.name, - code: attribute.code, + name: attribute.name.toLowerCase(), + code: attribute.code.toLowerCase(), origin: tenant.features[0].certifierId, description: attribute.description, }, @@ -424,7 +438,12 @@ describe("database test", () => { serviceName: "", } ) - ).rejects.toThrowError(attributeDuplicate(attribute.name)); + ).rejects.toThrowError( + attributeDuplicateByNameAndCode( + attribute.name.toLowerCase(), + attribute.code.toLowerCase() + ) + ); }); }); }); From ab69f335ad73580cabbb426a8535704e12a2db4c Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 5 Nov 2024 15:12:40 +0100 Subject: [PATCH 006/126] IMN-639 - Creating JWKS clients only once at services startup (#1131) --- .../bff/authorization/get Session Token.bru | 2 +- collections/environments/PagoPA local.bru | 4 ++-- packages/agreement-process/src/app.ts | 5 ++++- packages/api-gateway/src/app.ts | 5 ++++- packages/attribute-registry-process/src/app.ts | 5 ++++- packages/authorization-process/src/app.ts | 5 ++++- packages/backend-for-frontend/src/app.ts | 15 ++++++++++++--- .../src/routers/authorizationRouter.ts | 7 +++++-- .../src/routers/supportRouter.ts | 7 +++++-- .../src/services/authorizationService.ts | 7 +++---- packages/catalog-process/src/app.ts | 5 ++++- .../commons/src/auth/authenticationMiddleware.ts | 9 ++++----- packages/commons/src/auth/jwk.ts | 6 +++--- packages/purpose-process/src/app.ts | 5 ++++- packages/tenant-process/src/app.ts | 5 ++++- 15 files changed, 63 insertions(+), 29 deletions(-) diff --git a/collections/bff/authorization/get Session Token.bru b/collections/bff/authorization/get Session Token.bru index 80a82bb2e4..7a2e334f80 100644 --- a/collections/bff/authorization/get Session Token.bru +++ b/collections/bff/authorization/get Session Token.bru @@ -17,7 +17,7 @@ headers { body:json { { - "identity_token": {{JWT}} + "identity_token": "{{process.env.JWT}}" } } diff --git a/collections/environments/PagoPA local.bru b/collections/environments/PagoPA local.bru index 2a084277c7..db3d9de5bb 100644 --- a/collections/environments/PagoPA local.bru +++ b/collections/environments/PagoPA local.bru @@ -1,6 +1,6 @@ vars { host-agreement: http://localhost:3100 - JWT: {{process.env.JWT}} + JWT: Bearer {{process.env.JWT}} host-catalog: http://localhost:3000 correlation-id: 79eb4963-3866-422f-8ef0-87871e5f9a71 JWT-Invalid: @@ -10,5 +10,5 @@ vars { host-purpose: http://localhost:3400 host-authorization: http://localhost:3300 host-api-gw: http://localhost:3700/api-gateway/0.0 - JWT-M2M: {{process.env.JWT-M2M}} + JWT-M2M: Bearer {{process.env.JWT-M2M}} } diff --git a/packages/agreement-process/src/app.ts b/packages/agreement-process/src/app.ts index d56584d362..7b22271360 100644 --- a/packages/agreement-process/src/app.ts +++ b/packages/agreement-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "agreement-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // 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(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(agreementRouter(zodiosCtx)); diff --git a/packages/api-gateway/src/app.ts b/packages/api-gateway/src/app.ts index a1e1f1c974..b9c16f7157 100644 --- a/packages/api-gateway/src/app.ts +++ b/packages/api-gateway/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, initRedisRateLimiter, loggerMiddleware, @@ -17,6 +18,8 @@ const clients = getInteropBeClients(); const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + const redisRateLimiter = await initRedisRateLimiter({ limiterGroup: "API_GW", maxRequests: config.rateLimiterMaxRequests, @@ -37,7 +40,7 @@ app.use( `/api-gateway/${config.apiGatewayInterfaceVersion}`, healthRouter, contextMiddleware(serviceName, false), - authenticationMiddleware(config), + authenticationMiddleware(config, jwksClients), // Authenticated routes - rate limiter relies on auth data to work rateLimiterMiddleware(redisRateLimiter), apiGatewayRouter(zodiosCtx, clients) diff --git a/packages/attribute-registry-process/src/app.ts b/packages/attribute-registry-process/src/app.ts index 0f5886ed61..4ae8948925 100644 --- a/packages/attribute-registry-process/src/app.ts +++ b/packages/attribute-registry-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "attribute-registry-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // 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(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(attributeRouter(zodiosCtx)); diff --git a/packages/authorization-process/src/app.ts b/packages/authorization-process/src/app.ts index d7cb5b9671..8ef53acabb 100644 --- a/packages/authorization-process/src/app.ts +++ b/packages/authorization-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, zodiosCtx, } from "pagopa-interop-commons"; @@ -11,13 +12,15 @@ const serviceName = "authorization-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // 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(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(authorizationRouter(zodiosCtx)); export default app; diff --git a/packages/backend-for-frontend/src/app.ts b/packages/backend-for-frontend/src/app.ts index 7bb8946839..c0413917ee 100644 --- a/packages/backend-for-frontend/src/app.ts +++ b/packages/backend-for-frontend/src/app.ts @@ -6,6 +6,7 @@ import { zodiosCtx, initRedisRateLimiter, rateLimiterMiddleware, + buildJwksClients, } from "pagopa-interop-commons"; import express from "express"; import { config } from "./config/config.js"; @@ -37,6 +38,8 @@ const clients = getInteropBeClients(); const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + const redisRateLimiter = await initRedisRateLimiter({ limiterGroup: "BFF", maxRequests: config.rateLimiterMaxRequests, @@ -66,8 +69,14 @@ app.use( `/backend-for-frontend/${config.backendForFrontendInterfaceVersion}`, healthRouter, contextMiddleware(serviceName, false), - authorizationRouter(zodiosCtx, clients, allowList, redisRateLimiter), - authenticationMiddleware(config), + authorizationRouter( + zodiosCtx, + clients, + allowList, + redisRateLimiter, + jwksClients + ), + authenticationMiddleware(config, jwksClients), // Authenticated routes - rate limiter relies on auth data to work rateLimiterMiddleware(redisRateLimiter), catalogRouter(zodiosCtx, clients, fileManager), @@ -75,7 +84,7 @@ app.use( purposeRouter(zodiosCtx, clients), agreementRouter(zodiosCtx, clients, fileManager), selfcareRouter(clients, zodiosCtx), - supportRouter(zodiosCtx, clients, redisRateLimiter), + supportRouter(zodiosCtx, clients, redisRateLimiter, jwksClients), toolRouter(zodiosCtx, clients), tenantRouter(zodiosCtx, clients), clientRouter(zodiosCtx, clients), diff --git a/packages/backend-for-frontend/src/routers/authorizationRouter.ts b/packages/backend-for-frontend/src/routers/authorizationRouter.ts index ecbb4baa0f..98dcadf818 100644 --- a/packages/backend-for-frontend/src/routers/authorizationRouter.ts +++ b/packages/backend-for-frontend/src/routers/authorizationRouter.ts @@ -8,6 +8,7 @@ import { zodiosValidationErrorToApiProblem, RateLimiter, rateLimiterHeadersFromStatus, + buildJwksClients, } from "pagopa-interop-commons"; import { tooManyRequestsError } from "pagopa-interop-models"; import { makeApiProblem } from "../model/errors.js"; @@ -21,7 +22,8 @@ const authorizationRouter = ( ctx: ZodiosContext, { tenantProcessClient }: PagoPAInteropBeClients, allowList: string[], - rateLimiter: RateLimiter + rateLimiter: RateLimiter, + jwksClients: ReturnType ): ZodiosRouter => { const authorizationRouter = ctx.router(bffApi.authorizationApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, @@ -32,7 +34,8 @@ const authorizationRouter = ( interopTokenGenerator, tenantProcessClient, allowList, - rateLimiter + rateLimiter, + jwksClients ); authorizationRouter diff --git a/packages/backend-for-frontend/src/routers/supportRouter.ts b/packages/backend-for-frontend/src/routers/supportRouter.ts index 847024ea0f..e4dd117675 100644 --- a/packages/backend-for-frontend/src/routers/supportRouter.ts +++ b/packages/backend-for-frontend/src/routers/supportRouter.ts @@ -1,6 +1,7 @@ import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ZodiosRouter } from "@zodios/express"; import { + buildJwksClients, ExpressContext, InteropTokenGenerator, RateLimiter, @@ -19,7 +20,8 @@ import { fromBffAppContext } from "../utilities/context.js"; const supportRouter = ( ctx: ZodiosContext, { tenantProcessClient }: PagoPAInteropBeClients, - rateLimiter: RateLimiter + rateLimiter: RateLimiter, + jwksClients: ReturnType ): ZodiosRouter => { const supportRouter = ctx.router(bffApi.supportApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, @@ -30,7 +32,8 @@ const supportRouter = ( interopTokenGenerator, tenantProcessClient, config.tenantAllowedOrigins, - rateLimiter + rateLimiter, + jwksClients ); supportRouter.post("/session/saml2/tokens", async (req, res) => { diff --git a/packages/backend-for-frontend/src/services/authorizationService.ts b/packages/backend-for-frontend/src/services/authorizationService.ts index a5ae88b26c..4c317539fa 100644 --- a/packages/backend-for-frontend/src/services/authorizationService.ts +++ b/packages/backend-for-frontend/src/services/authorizationService.ts @@ -16,7 +16,7 @@ import { USER_ROLES, WithLogger, decodeJwtToken, - getJwksClients, + buildJwksClients, userRoles, verifyJwtToken, } from "pagopa-interop-commons"; @@ -52,10 +52,9 @@ export function authorizationServiceBuilder( interopTokenGenerator: InteropTokenGenerator, tenantProcessClient: PagoPAInteropBeClients["tenantProcessClient"], allowList: string[], - rateLimiter: RateLimiter + rateLimiter: RateLimiter, + jwksClients: ReturnType ) { - const jwksClients = getJwksClients(config); - const readJwt = async ( identityToken: string, logger: Logger diff --git a/packages/catalog-process/src/app.ts b/packages/catalog-process/src/app.ts index 830ec7acdb..7966b61e53 100644 --- a/packages/catalog-process/src/app.ts +++ b/packages/catalog-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "catalog-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // 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(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(eservicesRouter(zodiosCtx)); diff --git a/packages/commons/src/auth/authenticationMiddleware.ts b/packages/commons/src/auth/authenticationMiddleware.ts index aa75eda7c9..dcc081a86e 100644 --- a/packages/commons/src/auth/authenticationMiddleware.ts +++ b/packages/commons/src/auth/authenticationMiddleware.ts @@ -5,10 +5,10 @@ import { unauthorizedError, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { JwksClient } from "jwks-rsa"; import { ExpressContext, fromAppContext, - getJwksClients, JWTConfig, jwtFromAuthHeader, } from "../index.js"; @@ -18,17 +18,16 @@ import { readAuthDataFromJwtToken, verifyJwtToken } from "./jwt.js"; const makeApiProblem = makeApiProblemBuilder({}); export const authenticationMiddleware: ( - config: JWTConfig + config: JWTConfig, + jwksClients: JwksClient[] ) => ZodiosRouterContextRequestHandler = - (config: JWTConfig) => + (config: JWTConfig, jwksClients: JwksClient[]) => async (req, res, next): Promise => { // We assume that: // - contextMiddleware already set ctx.serviceName and ctx.correlationId const ctx = fromAppContext(req.ctx); try { - const jwksClients = getJwksClients(config); - const jwtToken = jwtFromAuthHeader(req, ctx.logger); const valid = await verifyJwtToken( jwtToken, diff --git a/packages/commons/src/auth/jwk.ts b/packages/commons/src/auth/jwk.ts index a38ef5ff67..4ac6dd3567 100644 --- a/packages/commons/src/auth/jwk.ts +++ b/packages/commons/src/auth/jwk.ts @@ -67,14 +67,14 @@ export function sortJWK(jwk: JsonWebKey): JsonWebKey { ); } -export function getJwksClients(config: JWTConfig): JwksClient[] { +export function buildJwksClients(config: JWTConfig): JwksClient[] { return config.wellKnownUrls.map((url) => jwksClient({ cache: true, rateLimit: true, jwksUri: url, - /* If JWKS_CACHE_MAX_AGE_MILLIS not provided using 10 minute like default value: - /~https://github.com/auth0/node-jwks-rsa/blob/master/EXAMPLES.md#configuration + /* If JWKS_CACHE_MAX_AGE_MILLIS not provided using 10 minutes as default value: + /~https://github.com/auth0/node-jwks-rsa/blob/master/EXAMPLES.md#configuration */ cacheMaxAge: config.jwksCacheMaxAge ?? 600000, }) diff --git a/packages/purpose-process/src/app.ts b/packages/purpose-process/src/app.ts index 6ca56e43f2..b9ace41e7c 100644 --- a/packages/purpose-process/src/app.ts +++ b/packages/purpose-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "purpose-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // 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(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(purposeRouter(zodiosCtx)); diff --git a/packages/tenant-process/src/app.ts b/packages/tenant-process/src/app.ts index 218b8d6fc7..3a506b300a 100644 --- a/packages/tenant-process/src/app.ts +++ b/packages/tenant-process/src/app.ts @@ -1,5 +1,6 @@ import { authenticationMiddleware, + buildJwksClients, contextMiddleware, loggerMiddleware, zodiosCtx, @@ -12,13 +13,15 @@ const serviceName = "tenant-process"; const app = zodiosCtx.app(); +const jwksClients = buildJwksClients(config); + // 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(healthRouter); app.use(contextMiddleware(serviceName)); -app.use(authenticationMiddleware(config)); +app.use(authenticationMiddleware(config, jwksClients)); app.use(loggerMiddleware(serviceName)); app.use(tenantRouter(zodiosCtx)); From 49ba3aa1f055a1e51d074eede659abce96fb10e8 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Tue, 5 Nov 2024 15:27:02 +0100 Subject: [PATCH 007/126] Add strict to tsconfig (#1124) --- package.json | 1 + packages/agreement-email-sender/package.json | 4 +- packages/agreement-lifecycle/package.json | 2 +- .../agreement-outbound-writer/package.json | 4 +- packages/agreement-process/package.json | 4 +- .../agreement-readmodel-writer/package.json | 4 +- .../package.json | 4 +- packages/api-clients/package.json | 6 +- packages/api-gateway/package.json | 4 +- .../attribute-registry-process/package.json | 4 +- .../package.json | 4 +- packages/authorization-process/package.json | 4 +- packages/authorization-updater/package.json | 4 +- packages/backend-for-frontend/package.json | 4 +- packages/catalog-outbound-writer/package.json | 4 +- .../catalog-platformstate-writer/package.json | 5 +- packages/catalog-process/package.json | 4 +- .../catalog-readmodel-writer/package.json | 4 +- packages/client-readmodel-writer/package.json | 4 +- packages/commons/package.json | 2 +- .../compute-agreements-consumer/package.json | 4 +- packages/datalake-data-export/package.json | 4 +- .../package.json | 4 +- packages/event-migration/package.json | 6 +- .../package.json | 4 +- packages/key-readmodel-writer/package.json | 4 +- packages/models/package.json | 2 +- packages/notifier-seeder/package.json | 4 +- packages/one-trust-notices/package.json | 2 +- packages/pn-consumers/package.json | 4 +- .../producer-key-events-writer/package.json | 4 +- .../package.json | 4 +- .../package.json | 4 +- packages/purpose-outbound-writer/package.json | 4 +- .../purpose-platformstate-writer/package.json | 4 +- packages/purpose-process/package.json | 4 +- .../purpose-readmodel-writer/package.json | 4 +- .../selfcare-onboarding-consumer/package.json | 4 +- packages/tenant-outbound-writer/package.json | 4 +- packages/tenant-process/package.json | 4 +- packages/tenant-readmodel-writer/package.json | 4 +- pnpm-lock.yaml | 532 +++++++++++++----- tsconfig.json | 14 +- 43 files changed, 479 insertions(+), 225 deletions(-) diff --git a/package.json b/package.json index bbc23cfd29..28b56b7155 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "prune-modules": "find . -name 'node_modules' -type d -exec rm -rf {} +" }, "devDependencies": { + "@tsconfig/strictest": "2.0.5", "@tsconfig/node-lts": "20.1.3", "turbo": "2.0.4" }, diff --git a/packages/agreement-email-sender/package.json b/packages/agreement-email-sender/package.json index bfb9359b03..ea90b18e14 100644 --- a/packages/agreement-email-sender/package.json +++ b/packages/agreement-email-sender/package.json @@ -15,7 +15,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, @@ -30,7 +30,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/agreement-lifecycle/package.json b/packages/agreement-lifecycle/package.json index 7cf5fa3890..c359fe25f0 100644 --- a/packages/agreement-lifecycle/package.json +++ b/packages/agreement-lifecycle/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm --watch ./src/index.ts", + "start": "tsx --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 29903c6541..8879504385 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index 980c7b3721..c405df5ad4 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, @@ -30,7 +30,7 @@ "prettier": "2.8.8", "puppeteer": "22.11.2", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 9fb6785dcc..42c5db8fab 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0", "@anatine/zod-mock": "3.13.4" diff --git a/packages/anac-certified-attributes-importer/package.json b/packages/anac-certified-attributes-importer/package.json index a78f5ad664..3a58c19dfd 100644 --- a/packages/anac-certified-attributes-importer/package.json +++ b/packages/anac-certified-attributes-importer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/api-clients/package.json b/packages/api-clients/package.json index 20145f6472..71a102498f 100644 --- a/packages/api-clients/package.json +++ b/packages/api-clients/package.json @@ -13,10 +13,10 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", - "generate-model": "node --loader ts-node/esm ./generate.ts", + "generate-model": "tsx ./generate.ts", "clean-generated": "pnpm exec rm ./src/generated/*.ts" }, "keywords": [], @@ -31,7 +31,7 @@ "openapi-zod-client": "1.18.1", "openapi3-ts": "3.1.0", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5" }, "dependencies": { diff --git a/packages/api-gateway/package.json b/packages/api-gateway/package.json index 5de2d0ef53..4eb73fdf98 100644 --- a/packages/api-gateway/package.json +++ b/packages/api-gateway/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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/attribute-registry-process/package.json b/packages/attribute-registry-process/package.json index 9725a194c8..2d12271055 100644 --- a/packages/attribute-registry-process/package.json +++ b/packages/attribute-registry-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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -27,7 +27,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/attribute-registry-readmodel-writer/package.json b/packages/attribute-registry-readmodel-writer/package.json index 319208b94e..fa5ffab59a 100644 --- a/packages/attribute-registry-readmodel-writer/package.json +++ b/packages/attribute-registry-readmodel-writer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -24,7 +24,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/authorization-process/package.json b/packages/authorization-process/package.json index 27f0127472..d39fcc4589 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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -24,7 +24,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/authorization-updater/package.json b/packages/authorization-updater/package.json index d703d3b783..f585c6c187 100644 --- a/packages/authorization-updater/package.json +++ b/packages/authorization-updater/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", "test": "vitest", @@ -28,7 +28,7 @@ "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/backend-for-frontend/package.json b/packages/backend-for-frontend/package.json index 7044893c23..17a7cde33c 100644 --- a/packages/backend-for-frontend/package.json +++ b/packages/backend-for-frontend/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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -27,7 +27,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 8e19ced827..edd098d26b 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-platformstate-writer/package.json b/packages/catalog-platformstate-writer/package.json index 4a720b21c3..4e7eb78540 100644 --- a/packages/catalog-platformstate-writer/package.json +++ b/packages/catalog-platformstate-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,8 @@ "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "testcontainers": "10.9.0", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-process/package.json b/packages/catalog-process/package.json index 769b1246b8..bc2046ee62 100644 --- a/packages/catalog-process/package.json +++ b/packages/catalog-process/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -30,7 +30,7 @@ "pg-promise": "11.8.0", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/catalog-readmodel-writer/package.json b/packages/catalog-readmodel-writer/package.json index 8a6e112fd4..1274d02a7f 100644 --- a/packages/catalog-readmodel-writer/package.json +++ b/packages/catalog-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/client-readmodel-writer/package.json b/packages/client-readmodel-writer/package.json index 6b5a59464d..e00bed02d8 100644 --- a/packages/client-readmodel-writer/package.json +++ b/packages/client-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/commons/package.json b/packages/commons/package.json index a2d1cf32a8..1c9c3bb1fe 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm --watch ./src/index.ts", + "start": "tsx --watch ./src/index.ts", "build": "tsc && pnpm run copy-sql-files && cpx './src/pdf-generator/paged.polyfill.js' './dist/pdf-generator'", "check": "tsc --project tsconfig.check.json", "copy-sql-files": "cp ./src/repositories/sql/*.sql dist/repositories/sql" diff --git a/packages/compute-agreements-consumer/package.json b/packages/compute-agreements-consumer/package.json index b2b6d22bb8..cb86bd49c7 100644 --- a/packages/compute-agreements-consumer/package.json +++ b/packages/compute-agreements-consumer/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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -21,7 +21,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5" }, "dependencies": { diff --git a/packages/datalake-data-export/package.json b/packages/datalake-data-export/package.json index d9ab0456b6..171b7c87ac 100644 --- a/packages/datalake-data-export/package.json +++ b/packages/datalake-data-export/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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -22,7 +22,7 @@ "@pagopa/eslint-config": "3.0.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/eservice-descriptors-archiver/package.json b/packages/eservice-descriptors-archiver/package.json index c81de847eb..79c46dda91 100644 --- a/packages/eservice-descriptors-archiver/package.json +++ b/packages/eservice-descriptors-archiver/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "openapi-zod-client": "1.18.1", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/event-migration/package.json b/packages/event-migration/package.json index 5397a2de49..4fbc6c7904 100644 --- a/packages/event-migration/package.json +++ b/packages/event-migration/package.json @@ -10,8 +10,8 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start:migrate": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", - "start:verify": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", + "start:migrate": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "start:verify": "tsx -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", "build": "tsc" }, "keywords": [], @@ -36,7 +36,7 @@ "@types/lodash.isequal": "4.5.8", "eslint": "8.57.0", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" } diff --git a/packages/ivass-certified-attributes-importer/package.json b/packages/ivass-certified-attributes-importer/package.json index a4866577fa..871a82cfab 100644 --- a/packages/ivass-certified-attributes-importer/package.json +++ b/packages/ivass-certified-attributes-importer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "@types/adm-zip": "0.5.5", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/key-readmodel-writer/package.json b/packages/key-readmodel-writer/package.json index af4f12b037..6325fa4784 100644 --- a/packages/key-readmodel-writer/package.json +++ b/packages/key-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/models/package.json b/packages/models/package.json index f3ab5d994d..48deee10fe 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -13,7 +13,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm --watch ./src/index.ts", + "start": "tsx --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json", "generate-protobuf": "mkdirp ./src/gen && npx protoc --ts_opt=eslint_disable --ts_out ./src/gen --proto_path ./proto ./proto/**/**/*.proto && tsc-esm-fix --src='src/gen/' --ext='.js'" diff --git a/packages/notifier-seeder/package.json b/packages/notifier-seeder/package.json index ac3c5213a5..0df35f6735 100644 --- a/packages/notifier-seeder/package.json +++ b/packages/notifier-seeder/package.json @@ -15,7 +15,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "kafkajs": "2.2.4", "mkdirp": "3.0.1", diff --git a/packages/one-trust-notices/package.json b/packages/one-trust-notices/package.json index 3b68f5f4ed..73df7d6445 100644 --- a/packages/one-trust-notices/package.json +++ b/packages/one-trust-notices/package.json @@ -24,7 +24,7 @@ "@types/lodash": "4.14.196", "@types/node": "20.4.9", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5" }, "dependencies": { diff --git a/packages/pn-consumers/package.json b/packages/pn-consumers/package.json index 1d016ccfe2..bea8e6aa6d 100644 --- a/packages/pn-consumers/package.json +++ b/packages/pn-consumers/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/producer-key-events-writer/package.json b/packages/producer-key-events-writer/package.json index 8b1c51a2d5..1bdefcae1c 100644 --- a/packages/producer-key-events-writer/package.json +++ b/packages/producer-key-events-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -24,7 +24,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/producer-key-readmodel-writer/package.json b/packages/producer-key-readmodel-writer/package.json index da8bfaa6d4..a5df018a6f 100644 --- a/packages/producer-key-readmodel-writer/package.json +++ b/packages/producer-key-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/producer-keychain-readmodel-writer/package.json b/packages/producer-keychain-readmodel-writer/package.json index d73caa1024..f9119fae85 100644 --- a/packages/producer-keychain-readmodel-writer/package.json +++ b/packages/producer-keychain-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 5b2830f708..244b9faa3f 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-platformstate-writer/package.json b/packages/purpose-platformstate-writer/package.json index b17c00dde4..0b441191da 100644 --- a/packages/purpose-platformstate-writer/package.json +++ b/packages/purpose-platformstate-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-process/package.json b/packages/purpose-process/package.json index 9c2eaec3c4..9c6af53d9a 100644 --- a/packages/purpose-process/package.json +++ b/packages/purpose-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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, @@ -26,7 +26,7 @@ "prettier": "2.8.8", "puppeteer": "22.11.2", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/purpose-readmodel-writer/package.json b/packages/purpose-readmodel-writer/package.json index cbdabd96f1..328de3bf3d 100644 --- a/packages/purpose-readmodel-writer/package.json +++ b/packages/purpose-readmodel-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/selfcare-onboarding-consumer/package.json b/packages/selfcare-onboarding-consumer/package.json index e1eb6bf376..cc5dad5ac9 100644 --- a/packages/selfcare-onboarding-consumer/package.json +++ b/packages/selfcare-onboarding-consumer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 190a113491..668b914f54 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -9,7 +9,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/tenant-process/package.json b/packages/tenant-process/package.json index 1ec9be60ad..858d3d4f2e 100644 --- a/packages/tenant-process/package.json +++ b/packages/tenant-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 --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "pg-promise": "11.8.0", "prettier": "2.8.8", "testcontainers": "10.9.0", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/tenant-readmodel-writer/package.json b/packages/tenant-readmodel-writer/package.json index 0764fccd60..ecab1ba030 100644 --- a/packages/tenant-readmodel-writer/package.json +++ b/packages/tenant-readmodel-writer/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -23,7 +23,7 @@ "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9dd9ae6c53..cd0b0f8a82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@tsconfig/node-lts': specifier: 20.1.3 version: 20.1.3 + '@tsconfig/strictest': + specifier: 2.0.5 + version: 2.0.5 turbo: specifier: 2.0.4 version: 2.0.4 @@ -63,9 +66,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -143,9 +146,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -286,9 +289,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -344,9 +347,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -396,9 +399,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -457,9 +460,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -512,9 +515,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -594,9 +597,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -649,9 +652,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -722,9 +725,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -786,9 +789,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -886,9 +889,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -947,9 +950,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1008,9 +1011,12 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1090,9 +1096,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1148,9 +1154,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1243,9 +1249,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1458,9 +1464,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1498,9 +1504,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1599,9 +1605,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1660,9 +1666,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1724,9 +1730,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1798,9 +1804,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1902,9 +1908,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1951,9 +1957,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.4.9)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2000,9 +2006,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2049,9 +2055,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2101,9 +2107,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2153,9 +2159,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2214,9 +2220,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2275,9 +2281,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2354,9 +2360,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2406,9 +2412,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2455,9 +2461,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2516,9 +2522,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2586,9 +2592,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2638,9 +2644,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -6018,6 +6024,15 @@ packages: dev: true optional: true + /@esbuild/aix-ppc64@0.23.1: + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm64@0.21.5: resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} @@ -6027,6 +6042,15 @@ packages: dev: true optional: true + /@esbuild/android-arm64@0.23.1: + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-arm@0.21.5: resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} @@ -6036,6 +6060,15 @@ packages: dev: true optional: true + /@esbuild/android-arm@0.23.1: + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/android-x64@0.21.5: resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} @@ -6045,6 +6078,15 @@ packages: dev: true optional: true + /@esbuild/android-x64@0.23.1: + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-arm64@0.21.5: resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} @@ -6054,6 +6096,15 @@ packages: dev: true optional: true + /@esbuild/darwin-arm64@0.23.1: + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/darwin-x64@0.21.5: resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} @@ -6063,6 +6114,15 @@ packages: dev: true optional: true + /@esbuild/darwin-x64@0.23.1: + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-arm64@0.21.5: resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} @@ -6072,6 +6132,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-arm64@0.23.1: + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/freebsd-x64@0.21.5: resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} @@ -6081,6 +6150,15 @@ packages: dev: true optional: true + /@esbuild/freebsd-x64@0.23.1: + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm64@0.21.5: resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} @@ -6090,6 +6168,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm64@0.23.1: + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-arm@0.21.5: resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} @@ -6099,6 +6186,15 @@ packages: dev: true optional: true + /@esbuild/linux-arm@0.23.1: + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ia32@0.21.5: resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} @@ -6108,6 +6204,15 @@ packages: dev: true optional: true + /@esbuild/linux-ia32@0.23.1: + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64@0.21.5: resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} @@ -6117,6 +6222,15 @@ packages: dev: true optional: true + /@esbuild/linux-loong64@0.23.1: + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-mips64el@0.21.5: resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} @@ -6126,6 +6240,15 @@ packages: dev: true optional: true + /@esbuild/linux-mips64el@0.23.1: + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-ppc64@0.21.5: resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} @@ -6135,6 +6258,15 @@ packages: dev: true optional: true + /@esbuild/linux-ppc64@0.23.1: + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-riscv64@0.21.5: resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} @@ -6144,6 +6276,15 @@ packages: dev: true optional: true + /@esbuild/linux-riscv64@0.23.1: + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-s390x@0.21.5: resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} @@ -6153,6 +6294,15 @@ packages: dev: true optional: true + /@esbuild/linux-s390x@0.23.1: + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-x64@0.21.5: resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} @@ -6162,6 +6312,15 @@ packages: dev: true optional: true + /@esbuild/linux-x64@0.23.1: + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/netbsd-x64@0.21.5: resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} @@ -6171,6 +6330,24 @@ packages: dev: true optional: true + /@esbuild/netbsd-x64@0.23.1: + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-arm64@0.23.1: + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/openbsd-x64@0.21.5: resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} @@ -6180,6 +6357,15 @@ packages: dev: true optional: true + /@esbuild/openbsd-x64@0.23.1: + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /@esbuild/sunos-x64@0.21.5: resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} @@ -6189,6 +6375,15 @@ packages: dev: true optional: true + /@esbuild/sunos-x64@0.23.1: + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-arm64@0.21.5: resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} @@ -6198,6 +6393,15 @@ packages: dev: true optional: true + /@esbuild/win32-arm64@0.23.1: + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-ia32@0.21.5: resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} @@ -6207,6 +6411,15 @@ packages: dev: true optional: true + /@esbuild/win32-ia32@0.23.1: + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@esbuild/win32-x64@0.21.5: resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} @@ -6216,6 +6429,15 @@ packages: dev: true optional: true + /@esbuild/win32-x64@0.23.1: + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: 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} @@ -7734,6 +7956,10 @@ packages: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} dev: true + /@tsconfig/strictest@2.0.5: + resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} + dev: true + /@types/adm-zip@0.5.5: resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} dependencies: @@ -9419,6 +9645,38 @@ packages: '@esbuild/win32-x64': 0.21.5 dev: true + /esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} @@ -10118,6 +10376,12 @@ packages: get-intrinsic: 1.2.4 dev: true + /get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + /get-uri@6.0.3: resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} engines: {node: '>= 14'} @@ -11884,6 +12148,10 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + /resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -12585,37 +12853,6 @@ packages: yn: 3.1.1 dev: true - /ts-node@10.9.2(@types/node@20.4.9)(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.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.4.9 - acorn: 8.12.1 - acorn-walk: 8.3.3 - 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.2.0: resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} @@ -12660,6 +12897,17 @@ packages: typescript: 5.4.5 dev: true + /tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /turbo-darwin-64@2.0.4: resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} cpu: [x64] diff --git a/tsconfig.json b/tsconfig.json index 9f28fb71a1..b5d495ded3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,13 @@ { - "extends": "@tsconfig/node-lts", + "extends": [ + "@tsconfig/node-lts/tsconfig.json", + "@tsconfig/strictest/tsconfig.json" + ], "compilerOptions": { - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "noUncheckedIndexedAccess": false, + "exactOptionalPropertyTypes": false, + "noImplicitOverride": false, + "checkJs": false, + "noPropertyAccessFromIndexSignature": false } } From fa71e251af46988823581afc62d4dddcbceb9175 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Tue, 5 Nov 2024 17:07:37 +0100 Subject: [PATCH 008/126] IMN 504 enable stricter peer deps 2 (#1150) Co-authored-by: Eric Camellini --- .npmrc | 2 + package.json | 2 +- packages/agreement-email-sender/Dockerfile | 1 + packages/agreement-outbound-writer/Dockerfile | 1 + .../agreement-outbound-writer/package.json | 1 + .../agreement-platformstate-writer/Dockerfile | 1 + packages/agreement-process/Dockerfile | 1 + packages/agreement-process/package.json | 1 + .../agreement-readmodel-writer/Dockerfile | 1 + .../agreement-readmodel-writer/package.json | 2 + .../Dockerfile | 1 + packages/api-clients/package.json | 1 + packages/api-gateway/Dockerfile | 1 + .../attribute-registry-process/Dockerfile | 1 + .../Dockerfile | 1 + packages/authorization-process/Dockerfile | 1 + packages/authorization-updater/Dockerfile | 3 +- packages/backend-for-frontend/Dockerfile | 1 + packages/catalog-outbound-writer/Dockerfile | 1 + packages/catalog-outbound-writer/package.json | 1 + .../catalog-platformstate-writer/Dockerfile | 1 + packages/catalog-process/Dockerfile | 1 + packages/catalog-readmodel-writer/Dockerfile | 1 + packages/client-readmodel-writer/Dockerfile | 1 + packages/commons-test/package.json | 1 + packages/commons/package.json | 1 + .../compute-agreements-consumer/Dockerfile | 1 + packages/datalake-data-export/Dockerfile | 1 + packages/dtd-catalog-exporter/Dockerfile | 3 +- .../eservice-descriptors-archiver/Dockerfile | 1 + .../package.json | 1 + .../Dockerfile | 1 + packages/kafka-iam-auth/package.json | 2 + packages/key-readmodel-writer/Dockerfile | 1 + packages/notifier-seeder/Dockerfile | 3 +- packages/one-trust-notices/Dockerfile | 3 +- packages/pn-consumers/Dockerfile | 1 + .../producer-key-events-writer/Dockerfile | 1 + .../producer-key-readmodel-writer/Dockerfile | 1 + .../Dockerfile | 1 + packages/purpose-outbound-writer/Dockerfile | 1 + packages/purpose-outbound-writer/package.json | 1 + .../purpose-platformstate-writer/Dockerfile | 1 + packages/purpose-process/Dockerfile | 1 + packages/purpose-readmodel-writer/Dockerfile | 1 + .../selfcare-onboarding-consumer/Dockerfile | 1 + packages/tenant-outbound-writer/Dockerfile | 1 + packages/tenant-outbound-writer/package.json | 1 + packages/tenant-process/Dockerfile | 1 + packages/tenant-readmodel-writer/Dockerfile | 1 + pnpm-lock.yaml | 11564 +++++++++------- 51 files changed, 6512 insertions(+), 5114 deletions(-) diff --git a/.npmrc b/.npmrc index cadcdac3a1..e0473968ca 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,3 @@ save-prefix= +auto-install-peers = false +strict-peer-dependencies = true diff --git a/package.json b/package.json index 28b56b7155..785be17b2b 100644 --- a/package.json +++ b/package.json @@ -56,5 +56,5 @@ "config": { "protocVersion": "26.1" }, - "packageManager": "pnpm@8.15.8" + "packageManager": "pnpm@9.12.3" } diff --git a/packages/agreement-email-sender/Dockerfile b/packages/agreement-email-sender/Dockerfile index bd0bca6b96..5711c74b4e 100644 --- a/packages/agreement-email-sender/Dockerfile +++ b/packages/agreement-email-sender/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-email-sender/package.json /app/packages/agreement-email-sender/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-outbound-writer/Dockerfile b/packages/agreement-outbound-writer/Dockerfile index a86919c53a..f60133f5d9 100644 --- a/packages/agreement-outbound-writer/Dockerfile +++ b/packages/agreement-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-outbound-writer/package.json /app/packages/agreement-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 8879504385..29c67eb697 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/agreement-platformstate-writer/Dockerfile b/packages/agreement-platformstate-writer/Dockerfile index cf634c5466..658a95d277 100644 --- a/packages/agreement-platformstate-writer/Dockerfile +++ b/packages/agreement-platformstate-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-platformstate-writer/package.json /app/packages/agreement-platformstate-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-process/Dockerfile b/packages/agreement-process/Dockerfile index eaeef21e16..a82a5121dd 100644 --- a/packages/agreement-process/Dockerfile +++ b/packages/agreement-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-process/package.json /app/packages/agreement-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index c405df5ad4..4638e17925 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -20,6 +20,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/express": "4.17.21", "@types/node": "20.14.6", diff --git a/packages/agreement-readmodel-writer/Dockerfile b/packages/agreement-readmodel-writer/Dockerfile index cbf15af562..46d3ebc355 100644 --- a/packages/agreement-readmodel-writer/Dockerfile +++ b/packages/agreement-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/agreement-readmodel-writer/package.json /app/packages/agreement-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/agreement-readmodel-writer/package.json b/packages/agreement-readmodel-writer/package.json index 42c5db8fab..9f5817d3ac 100644 --- a/packages/agreement-readmodel-writer/package.json +++ b/packages/agreement-readmodel-writer/package.json @@ -20,6 +20,8 @@ "author": "", "license": "Apache-2.0", "devDependencies": { + "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/anac-certified-attributes-importer/Dockerfile b/packages/anac-certified-attributes-importer/Dockerfile index d0bda046bc..ef615e0032 100644 --- a/packages/anac-certified-attributes-importer/Dockerfile +++ b/packages/anac-certified-attributes-importer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/anac-certified-attributes-importer/package.json /app/packages/anac-certified-attributes-importer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/api-clients/package.json b/packages/api-clients/package.json index 71a102498f..da7a2203d2 100644 --- a/packages/api-clients/package.json +++ b/packages/api-clients/package.json @@ -27,6 +27,7 @@ "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "@types/qs": "6.9.15", + "openapi-types": "12.1.3", "handlebars": "4.7.7", "openapi-zod-client": "1.18.1", "openapi3-ts": "3.1.0", diff --git a/packages/api-gateway/Dockerfile b/packages/api-gateway/Dockerfile index 03a589e9f7..937ba688df 100644 --- a/packages/api-gateway/Dockerfile +++ b/packages/api-gateway/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/api-gateway/package.json /app/packages/api-gateway/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/attribute-registry-process/Dockerfile b/packages/attribute-registry-process/Dockerfile index 4a6e86250a..411c711ac1 100644 --- a/packages/attribute-registry-process/Dockerfile +++ b/packages/attribute-registry-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/attribute-registry-process/package.json /app/packages/attribute-registry-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/attribute-registry-readmodel-writer/Dockerfile b/packages/attribute-registry-readmodel-writer/Dockerfile index 98c8532726..c040bf5a84 100644 --- a/packages/attribute-registry-readmodel-writer/Dockerfile +++ b/packages/attribute-registry-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/attribute-registry-readmodel-writer/package.json /app/packages/attribute-registry-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/authorization-process/Dockerfile b/packages/authorization-process/Dockerfile index 3c510caf3d..7547ac5e65 100644 --- a/packages/authorization-process/Dockerfile +++ b/packages/authorization-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/authorization-process/package.json /app/packages/authorization-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/authorization-updater/Dockerfile b/packages/authorization-updater/Dockerfile index e4d2b25875..ae4739d51e 100644 --- a/packages/authorization-updater/Dockerfile +++ b/packages/authorization-updater/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/authorization-updater/package.json /app/packages/authorization-updater/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -45,4 +46,4 @@ COPY --from=build /out /app WORKDIR /app/packages/authorization-updater EXPOSE 3000 -CMD ["node", "."] \ No newline at end of file +CMD ["node", "."] diff --git a/packages/backend-for-frontend/Dockerfile b/packages/backend-for-frontend/Dockerfile index 807bfd6854..8b179f6d24 100644 --- a/packages/backend-for-frontend/Dockerfile +++ b/packages/backend-for-frontend/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/backend-for-frontend/package.json /app/packages/backend-for-frontend/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-outbound-writer/Dockerfile b/packages/catalog-outbound-writer/Dockerfile index 693cc0c61b..e9e0bd6435 100644 --- a/packages/catalog-outbound-writer/Dockerfile +++ b/packages/catalog-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-outbound-writer/package.json /app/packages/catalog-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index edd098d26b..a9909a6a41 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/catalog-platformstate-writer/Dockerfile b/packages/catalog-platformstate-writer/Dockerfile index 071bcfce1d..fc7761b66b 100644 --- a/packages/catalog-platformstate-writer/Dockerfile +++ b/packages/catalog-platformstate-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-platformstate-writer/package.json /app/packages/catalog-platformstate-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-process/Dockerfile b/packages/catalog-process/Dockerfile index ff84d16c24..55cd103bd7 100644 --- a/packages/catalog-process/Dockerfile +++ b/packages/catalog-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-process/package.json /app/packages/catalog-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/catalog-readmodel-writer/Dockerfile b/packages/catalog-readmodel-writer/Dockerfile index 4cc4aabbe4..5a8ed0c001 100644 --- a/packages/catalog-readmodel-writer/Dockerfile +++ b/packages/catalog-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/catalog-readmodel-writer/package.json /app/packages/catalog-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/client-readmodel-writer/Dockerfile b/packages/client-readmodel-writer/Dockerfile index 2ccaf6387d..93d9aff7c4 100644 --- a/packages/client-readmodel-writer/Dockerfile +++ b/packages/client-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/client-readmodel-writer/package.json /app/packages/client-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/commons-test/package.json b/packages/commons-test/package.json index c8acc06d33..55e91be360 100644 --- a/packages/commons-test/package.json +++ b/packages/commons-test/package.json @@ -19,6 +19,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@aws-sdk/client-dynamodb": "3.637.0", "@aws-sdk/client-sesv2": "3.620.1", "@aws-sdk/util-dynamodb": "3.658.1", diff --git a/packages/commons/package.json b/packages/commons/package.json index 1c9c3bb1fe..ddc6b2af54 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -30,6 +30,7 @@ "connection-string": "4.4.0", "date-fns": "3.6.0", "date-fns-tz": "3.1.3", + "express": "4.20.0", "handlebars": "4.7.8", "jsonwebtoken": "9.0.2", "jwks-rsa": "3.1.0", diff --git a/packages/compute-agreements-consumer/Dockerfile b/packages/compute-agreements-consumer/Dockerfile index 6c3029c745..d84930276d 100644 --- a/packages/compute-agreements-consumer/Dockerfile +++ b/packages/compute-agreements-consumer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/compute-agreements-consumer/package.json /app/packages/compute-agreements-consumer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/datalake-data-export/Dockerfile b/packages/datalake-data-export/Dockerfile index 8d82627c69..60a2edcb6d 100644 --- a/packages/datalake-data-export/Dockerfile +++ b/packages/datalake-data-export/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/commons/package.json /app/packages/commons/package.json COPY ./packages/models/package.json /app/packages/models/package.json diff --git a/packages/dtd-catalog-exporter/Dockerfile b/packages/dtd-catalog-exporter/Dockerfile index 9908b28da3..488e526305 100644 --- a/packages/dtd-catalog-exporter/Dockerfile +++ b/packages/dtd-catalog-exporter/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/dtd-catalog-exporter/package.json /app/packages/dtd-catalog-exporter/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -38,4 +39,4 @@ COPY --from=build /out /app WORKDIR /app/packages/dtd-catalog-exporter EXPOSE 3000 -CMD [ "node", "." ] \ No newline at end of file +CMD [ "node", "." ] diff --git a/packages/eservice-descriptors-archiver/Dockerfile b/packages/eservice-descriptors-archiver/Dockerfile index a70ab83d1f..5e3af8ade1 100644 --- a/packages/eservice-descriptors-archiver/Dockerfile +++ b/packages/eservice-descriptors-archiver/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/eservice-descriptors-archiver/package.json /app/packages/eservice-descriptors-archiver/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/eservice-descriptors-archiver/package.json b/packages/eservice-descriptors-archiver/package.json index 79c46dda91..2a7ce3a8c6 100644 --- a/packages/eservice-descriptors-archiver/package.json +++ b/packages/eservice-descriptors-archiver/package.json @@ -31,6 +31,7 @@ }, "dependencies": { "@zodios/core": "10.9.6", + "axios": "1.7.4", "dotenv-flow": "4.1.0", "kafka-iam-auth": "workspace:*", "kafkajs": "2.2.4", diff --git a/packages/ivass-certified-attributes-importer/Dockerfile b/packages/ivass-certified-attributes-importer/Dockerfile index c8afa507c1..3623a3e3a9 100644 --- a/packages/ivass-certified-attributes-importer/Dockerfile +++ b/packages/ivass-certified-attributes-importer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/ivass-certified-attributes-importer/package.json /app/packages/ivass-certified-attributes-importer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/kafka-iam-auth/package.json b/packages/kafka-iam-auth/package.json index 1a1aa26661..7faa145774 100644 --- a/packages/kafka-iam-auth/package.json +++ b/packages/kafka-iam-auth/package.json @@ -18,6 +18,8 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/client-sso-oidc": "3.609.0", + "@aws-sdk/client-sts": "3.609.0", "aws-msk-iam-sasl-signer-js": "1.0.0", "kafkajs": "2.2.4", "pagopa-interop-commons": "workspace:*", diff --git a/packages/key-readmodel-writer/Dockerfile b/packages/key-readmodel-writer/Dockerfile index 1a9d23bbe1..8780ec0762 100644 --- a/packages/key-readmodel-writer/Dockerfile +++ b/packages/key-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/key-readmodel-writer/package.json /app/packages/key-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/notifier-seeder/Dockerfile b/packages/notifier-seeder/Dockerfile index a25d41560f..b1fc012d3f 100644 --- a/packages/notifier-seeder/Dockerfile +++ b/packages/notifier-seeder/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/notifier-seeder/package.json /app/packages/notifier-seeder/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -42,4 +43,4 @@ COPY --from=build /out /app WORKDIR /app/packages/notifier-seeder EXPOSE 3000 -CMD ["node", "."] \ No newline at end of file +CMD ["node", "."] diff --git a/packages/one-trust-notices/Dockerfile b/packages/one-trust-notices/Dockerfile index ba3d780407..5862581077 100644 --- a/packages/one-trust-notices/Dockerfile +++ b/packages/one-trust-notices/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/one-trust-notices/package.json /app/packages/one-trust-notices/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json @@ -38,4 +39,4 @@ COPY --from=build /out /app WORKDIR /app/packages/one-trust-notices EXPOSE 3000 -CMD [ "node", "." ] \ No newline at end of file +CMD [ "node", "." ] diff --git a/packages/pn-consumers/Dockerfile b/packages/pn-consumers/Dockerfile index 8429240973..dfa312051b 100644 --- a/packages/pn-consumers/Dockerfile +++ b/packages/pn-consumers/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/pn-consumers/package.json /app/packages/pn-consumers/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/producer-key-events-writer/Dockerfile b/packages/producer-key-events-writer/Dockerfile index c63c4e5bd8..59746502e2 100644 --- a/packages/producer-key-events-writer/Dockerfile +++ b/packages/producer-key-events-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/producer-key-events-writer/package.json /app/packages/producer-key-events-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/producer-key-readmodel-writer/Dockerfile b/packages/producer-key-readmodel-writer/Dockerfile index 0bd5604ba0..6a3b8e086b 100644 --- a/packages/producer-key-readmodel-writer/Dockerfile +++ b/packages/producer-key-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/producer-key-readmodel-writer/package.json /app/packages/producer-key-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/producer-keychain-readmodel-writer/Dockerfile b/packages/producer-keychain-readmodel-writer/Dockerfile index ee8a1cf6eb..73a71732ae 100644 --- a/packages/producer-keychain-readmodel-writer/Dockerfile +++ b/packages/producer-keychain-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/producer-keychain-readmodel-writer/package.json /app/packages/producer-keychain-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-outbound-writer/Dockerfile b/packages/purpose-outbound-writer/Dockerfile index 5ba3de5f46..b2dd44ec7b 100644 --- a/packages/purpose-outbound-writer/Dockerfile +++ b/packages/purpose-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-outbound-writer/package.json /app/packages/purpose-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 244b9faa3f..c3e80434a4 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/purpose-platformstate-writer/Dockerfile b/packages/purpose-platformstate-writer/Dockerfile index 2fa2593320..31aca4b36e 100644 --- a/packages/purpose-platformstate-writer/Dockerfile +++ b/packages/purpose-platformstate-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-platformstate-writer/package.json /app/packages/purpose-platformstate-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-process/Dockerfile b/packages/purpose-process/Dockerfile index ca6ff9bde1..e3fe734e97 100644 --- a/packages/purpose-process/Dockerfile +++ b/packages/purpose-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/purpose-process/package.json /app/packages/purpose-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/purpose-readmodel-writer/Dockerfile b/packages/purpose-readmodel-writer/Dockerfile index f0cec09bee..3f8e6ef1ce 100644 --- a/packages/purpose-readmodel-writer/Dockerfile +++ b/packages/purpose-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /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 diff --git a/packages/selfcare-onboarding-consumer/Dockerfile b/packages/selfcare-onboarding-consumer/Dockerfile index 8c1c3eec88..276aa61618 100644 --- a/packages/selfcare-onboarding-consumer/Dockerfile +++ b/packages/selfcare-onboarding-consumer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/selfcare-onboarding-consumer/package.json /app/packages/selfcare-onboarding-consumer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/tenant-outbound-writer/Dockerfile b/packages/tenant-outbound-writer/Dockerfile index 41c20db36d..28368098be 100644 --- a/packages/tenant-outbound-writer/Dockerfile +++ b/packages/tenant-outbound-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/tenant-outbound-writer/package.json /app/packages/tenant-outbound-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 668b914f54..6e79cc66a9 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -18,6 +18,7 @@ "license": "Apache-2.0", "devDependencies": { "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", "pagopa-interop-commons-test": "workspace:*", diff --git a/packages/tenant-process/Dockerfile b/packages/tenant-process/Dockerfile index 1e286d298b..cc78118316 100644 --- a/packages/tenant-process/Dockerfile +++ b/packages/tenant-process/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/tenant-process/package.json /app/packages/tenant-process/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/packages/tenant-readmodel-writer/Dockerfile b/packages/tenant-readmodel-writer/Dockerfile index 9314ddfda5..30b6de7616 100644 --- a/packages/tenant-readmodel-writer/Dockerfile +++ b/packages/tenant-readmodel-writer/Dockerfile @@ -6,6 +6,7 @@ WORKDIR /app COPY package.json /app/ COPY pnpm-lock.yaml /app/ COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ COPY ./packages/tenant-readmodel-writer/package.json /app/packages/tenant-readmodel-writer/package.json COPY ./packages/commons/package.json /app/packages/commons/package.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd0b0f8a82..e5cf7ee4f7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,7 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: - autoInstallPeers: true + autoInstallPeers: false excludeLinksFromLockfile: false importers: @@ -47,7 +47,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -131,9 +131,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -194,7 +197,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -224,7 +227,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -236,7 +239,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-agreement-lifecycle: specifier: workspace:* version: link:../agreement-lifecycle @@ -259,9 +262,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -332,9 +338,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -383,7 +392,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -441,7 +450,7 @@ importers: version: 10.1.0(openapi-types@12.1.3) '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -451,6 +460,9 @@ importers: handlebars: specifier: 4.7.7 version: 4.7.7 + openapi-types: + specifier: 12.1.3 + version: 12.1.3 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -474,7 +486,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -502,7 +514,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -532,7 +544,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -547,7 +559,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -578,7 +590,7 @@ importers: version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -639,7 +651,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -669,7 +681,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -684,7 +696,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -709,7 +721,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -776,7 +788,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -812,7 +824,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) adm-zip: specifier: 0.5.15 version: 0.5.15 @@ -864,7 +876,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 @@ -935,9 +947,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -998,7 +1013,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1031,7 +1046,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -1043,7 +1058,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -1071,7 +1086,7 @@ importers: version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1138,7 +1153,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1236,7 +1251,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1278,7 +1293,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -1291,6 +1306,9 @@ importers: date-fns-tz: specifier: 3.1.3 version: 3.1.3(date-fns@3.6.0) + express: + specifier: 4.20.0 + version: 4.20.0 handlebars: specifier: 4.7.8 version: 4.7.8 @@ -1305,7 +1323,7 @@ importers: version: 2.2.4 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) nodemailer: specifier: 6.9.14 version: 6.9.14 @@ -1376,9 +1394,12 @@ importers: '@aws-sdk/util-dynamodb': specifier: 3.658.1 version: 3.658.1(@aws-sdk/client-dynamodb@3.637.0) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1457,7 +1478,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1481,7 +1502,7 @@ importers: version: 4.1.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1494,7 +1515,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1521,7 +1542,7 @@ importers: version: 4.1.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1562,6 +1583,9 @@ importers: '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) + axios: + specifier: 1.7.4 + version: 1.7.4 dotenv-flow: specifier: 4.1.0 version: 4.1.0 @@ -1592,7 +1616,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1634,7 +1658,7 @@ importers: version: 4.5.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1711,7 +1735,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 @@ -1742,9 +1766,15 @@ importers: packages/kafka-iam-auth: dependencies: + '@aws-sdk/client-sso-oidc': + specifier: 3.609.0 + version: 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sts': + specifier: 3.609.0 + version: 3.609.0 aws-msk-iam-sasl-signer-js: specifier: 1.0.0 - version: 1.0.0(@aws-sdk/client-sso-oidc@3.645.0) + version: 1.0.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -1791,7 +1821,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1889,7 +1919,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1944,7 +1974,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/html2json': specifier: 1.0.1 version: 1.0.1 @@ -1990,7 +2020,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2045,7 +2075,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2094,7 +2124,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2146,7 +2176,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2205,9 +2235,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2268,7 +2301,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2298,7 +2331,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -2313,7 +2346,7 @@ importers: version: 4.7.8 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -2338,7 +2371,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -2399,7 +2432,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2454,7 +2487,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2507,9 +2540,12 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2539,7 +2575,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -2551,7 +2587,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -2573,7 +2609,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -2634,7 +2670,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2656,358 +2692,5180 @@ importers: packages: - /@ampproject/remapping@2.3.0: + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - /@anatine/zod-mock@3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8): + '@anatine/zod-mock@3.13.4': resolution: {integrity: sha512-yO/KeuyYsEDCTcQ+7CiRuY3dnafMHIZUMok6Ci7aERRCTQ+/XmsiPk/RnMx5wlLmWBTmX9kw+PavbMsjM+sAJA==} peerDependencies: '@faker-js/faker': ^7.0.0 || ^8.0.0 zod: ^3.21.4 - dependencies: - '@faker-js/faker': 8.4.1 - randexp: 0.5.3 - zod: 3.23.8 - dev: true - /@apidevtools/json-schema-ref-parser@9.0.6: + '@apidevtools/json-schema-ref-parser@9.0.6': resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} - dependencies: - '@jsdevtools/ono': 7.1.3 - call-me-maybe: 1.0.2 - js-yaml: 3.14.1 - /@apidevtools/openapi-schemas@2.1.0: + '@apidevtools/openapi-schemas@2.1.0': resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} engines: {node: '>=10'} - /@apidevtools/swagger-methods@3.0.2: + '@apidevtools/swagger-methods@3.0.2': resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3): + '@apidevtools/swagger-parser@10.1.0': resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} peerDependencies: openapi-types: '>=7' - dependencies: - '@apidevtools/json-schema-ref-parser': 9.0.6 - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - '@jsdevtools/ono': 7.1.3 - ajv: 8.16.0 - ajv-draft-04: 1.0.0(ajv@8.16.0) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 - /@aws-crypto/crc32@3.0.0: + '@aws-crypto/crc32@3.0.0': resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/crc32@5.2.0: + '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - tslib: 2.6.3 - dev: false - /@aws-crypto/crc32c@3.0.0: + '@aws-crypto/crc32c@3.0.0': resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/crc32c@5.2.0: + '@aws-crypto/crc32c@5.2.0': resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - tslib: 2.6.3 - dev: false - /@aws-crypto/ie11-detection@3.0.0: + '@aws-crypto/ie11-detection@3.0.0': resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} - dependencies: - tslib: 1.14.1 - dev: false - /@aws-crypto/sha1-browser@3.0.0: + '@aws-crypto/sha1-browser@3.0.0': resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==} - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha1-browser@5.2.0: + '@aws-crypto/sha1-browser@5.2.0': resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - dependencies: - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - dev: false - /@aws-crypto/sha256-browser@3.0.0: + '@aws-crypto/sha256-browser@3.0.0': resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-browser@5.2.0: + '@aws-crypto/sha256-browser@5.2.0': resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - dependencies: - '@aws-crypto/sha256-js': 5.2.0 - '@aws-crypto/supports-web-crypto': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - /@aws-crypto/sha256-js@3.0.0: + '@aws-crypto/sha256-js@3.0.0': resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-js@4.0.0: + '@aws-crypto/sha256-js@4.0.0': resolution: {integrity: sha512-MHGJyjE7TX9aaqXj7zk2ppnFUOhaDs5sP+HtNS0evOxn72c+5njUmyJmpGd7TfyoDznZlHMmdo/xGUdu2NIjNQ==} - dependencies: - '@aws-crypto/util': 4.0.0 - '@aws-sdk/types': 3.609.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-js@5.2.0: + '@aws-crypto/sha256-js@5.2.0': resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 - tslib: 2.6.3 - /@aws-crypto/supports-web-crypto@3.0.0: + '@aws-crypto/supports-web-crypto@3.0.0': resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==} - dependencies: - tslib: 1.14.1 - dev: false - /@aws-crypto/supports-web-crypto@5.2.0: + '@aws-crypto/supports-web-crypto@5.2.0': resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - dependencies: - tslib: 2.6.3 - /@aws-crypto/util@3.0.0: + '@aws-crypto/util@3.0.0': resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} - dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/util@4.0.0: + '@aws-crypto/util@4.0.0': resolution: {integrity: sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ==} - dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - dev: false - /@aws-crypto/util@5.2.0: + '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - /@aws-sdk/client-cognito-identity@3.609.0: + '@aws-sdk/client-cognito-identity@3.609.0': resolution: {integrity: sha512-3kDTpia1iN/accayoH3MbZRbDvX2tzrKrBTU7wNNoazVrh+gOMS8KCOWrOB72F0V299l4FsfQhnl9BDMVrc1iw==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/middleware-host-header': 3.609.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.609.0 - '@aws-sdk/middleware-user-agent': 3.609.0 - '@aws-sdk/region-config-resolver': 3.609.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.609.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - dev: false - /@aws-sdk/client-dynamodb@3.602.0: + '@aws-sdk/client-dynamodb@3.602.0': resolution: {integrity: sha512-q7lH7YD9KvHLF3tyAG1UqaPv4a6KiHLunqKYh8vt3d1WJK7t4wzE97Vf19MfNpza1MuZ0OF/SK8Kl69vEMrtOA==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-endpoint-discovery': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.2 - tslib: 2.6.3 - uuid: 9.0.1 - transitivePeerDependencies: - - aws-crt - dev: false - /@aws-sdk/client-dynamodb@3.637.0: + '@aws-sdk/client-dynamodb@3.637.0': resolution: {integrity: sha512-zUneT0yLgJjC69yry2fgYVWkv68OeV3amWaDXHirA8yJgygyc7tBLo+sQmtHczmKt8dBD9bU3OWpbAbtpF9Esw==} engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/client-sts': 3.637.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/middleware-endpoint-discovery': 3.620.0 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.637.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 + + '@aws-sdk/client-dynamodb@3.648.0': + resolution: {integrity: sha512-61yU6wQRlwOhD0mfJS/N8SYmv9hxkVYGKsXqSJ5PNNnySutoNof7cmX8cTuijpTQqLL9sKPfvPMlJCv7/M1AiA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-kms@3.600.0': + resolution: {integrity: sha512-m1o8aiVrVjExw6O+8JszXV3hr8sCyXKOLq1WCwWJqYF6Uf4vCf8iTYISQB3skbKUnBJm4SxVA82iViGAtWB7JA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-s3@3.387.0': + resolution: {integrity: sha512-TX42MDfXnIy/U6f8XjCTR1Ezg1125Sv5k9kdKZJ0kkKcb/81N0+RfTqsR+Kpn/AbLjtvSUWrFIdc1o//GsfZAQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/client-s3@3.600.0': + resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sesv2@3.620.1': + resolution: {integrity: sha512-pu/CbRQuxCA0EmDqyAktc77pjbmeWuQao6aLDBuXLDbccwKgRECLCTscqUcnqtfFhPkiOAim9LdczqEXLNIpcA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sqs@3.600.0': + resolution: {integrity: sha512-GgjEiWbGbiHGU3yZcCr1hXfaq/B/3ncYclqLEbbxrWkQOdri3qfl278h+Qn0/DQ8On0kj1UUxld87TVIkYfG8w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.600.0': + resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.609.0': + resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/client-sso-oidc@3.620.1': + resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.620.1 + + '@aws-sdk/client-sso-oidc@3.637.0': + resolution: {integrity: sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.637.0 + + '@aws-sdk/client-sso-oidc@3.645.0': + resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.645.0 + + '@aws-sdk/client-sso@3.387.0': + resolution: {integrity: sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/client-sso@3.598.0': + resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.609.0': + resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.620.1': + resolution: {integrity: sha512-4Ox0BSs+atrAhLvjNHN2uiYvSTdpMv//IS4l4XRoQG0cJKIPLs3OU3PL5H0X1NfZehz9/8FTWl5Lv81uw4j1eA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.637.0': + resolution: {integrity: sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.645.0': + resolution: {integrity: sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.387.0': + resolution: {integrity: sha512-RPOME0gpAViheH6xHyMg/XkE1G/fs6dgKK/NqlBZDjwMsSTPc8CmItEC6FOsCaLJktif0tD/u9m2uaQ4Lb1nVw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/client-sts@3.600.0': + resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.609.0': + resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.620.1': + resolution: {integrity: sha512-d+ECGFDg0IsDdmfKU2O0VeMYKZcmbfBaA9HkZnZ39wu1BlXGI73xJe8cfmzbobvu+Ly+bAfHdLCpgIY+pD4D7g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.637.0': + resolution: {integrity: sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.645.0': + resolution: {integrity: sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.598.0': + resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.609.0': + resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.620.1': + resolution: {integrity: sha512-6Ejce93dDlDnovl6oYtxj3I/SJMOQoFdmmtM4+4W/cgMWH+l00T5aszVxDLjjPfu3Ryt7dNhrXaYeK2Ue1ZBmg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.635.0': + resolution: {integrity: sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-cognito-identity@3.609.0': + resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.387.0': + resolution: {integrity: sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-env@3.598.0': + resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.609.0': + resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.620.1': + resolution: {integrity: sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.598.0': + resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.609.0': + resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.620.0': + resolution: {integrity: sha512-BI2BdrSKDmB/2ouB/NJR0PT0x/+5fmoF6XOE78hFBb4F5w/yynGgcJY936dF+oREfpME6ehjB2b0okGg78Scpw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.635.0': + resolution: {integrity: sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-ini@3.387.0': + resolution: {integrity: sha512-DS2Jg5E4Hd9fhJqTVNBG3SEwLwcyguPDcXSVCDz5pEHlYFM1U4x9b7aAbutzZujTH99MZ6Gua8kAotB/qjEjtw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-ini@3.598.0': + resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.598.0 + + '@aws-sdk/credential-provider-ini@3.609.0': + resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/credential-provider-ini@3.620.1': + resolution: {integrity: sha512-m9jwigMPRlRRhoPxCQZMOwQUd6imEJbksF6tSMYNae76DIvrCi4z2Jhp6RJ9Mij8cnewUZCAmvu2FlK9+n9M7A==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.620.1 + + '@aws-sdk/credential-provider-ini@3.637.0': + resolution: {integrity: sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.637.0 + + '@aws-sdk/credential-provider-ini@3.645.0': + resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.645.0 + + '@aws-sdk/credential-provider-node@3.387.0': + resolution: {integrity: sha512-NviQ0EqigPWwX4avKheRzE2R4YPzO6qzdyxKZUookr+uTWYroQ4ePZbHK1/BD8LlqKKBlttX/d3ENXjynU4clA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-node@3.600.0': + resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.609.0': + resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.620.1': + resolution: {integrity: sha512-KaprIJW2azM+oTIHi7S1ayJ3oQqoFwpMBWFpZM1nvSzaPucrZIUmX2m4uVrMM4LfXsfUsgMkrme2rBI1fGAjCg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.637.0': + resolution: {integrity: sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.645.0': + resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.387.0': + resolution: {integrity: sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-process@3.598.0': + resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.609.0': + resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.620.1': + resolution: {integrity: sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.387.0': + resolution: {integrity: sha512-sQgrEyTSrwLe8zgjP9VEUDz3dtGXSCc4k00bCwODbzdOWCA1nz9oF2tFmgjFsb1Q80pae01Pe50Esix5z2eHsQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-sso@3.598.0': + resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.609.0': + resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.620.1': + resolution: {integrity: sha512-cFU8e6ctdkWR8BRCnHFzs37N+ilbHf1OT2EeMjt1ZDE9FgTD5L5BTgVWDxnPmyQnEoBs1p4PyNPHkpHY5EmswQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.637.0': + resolution: {integrity: sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.645.0': + resolution: {integrity: sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.387.0': + resolution: {integrity: sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.598.0': + resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.598.0 + + '@aws-sdk/credential-provider-web-identity@3.609.0': + resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/credential-provider-web-identity@3.621.0': + resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.621.0 + + '@aws-sdk/credential-providers@3.609.0': + resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/endpoint-cache@3.572.0': + resolution: {integrity: sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.387.0': + resolution: {integrity: sha512-o7Dsq0YTUHFcKXD6+30/fXv/Wzdxqz9WonhCu3ZFPwTDLZgOM4QDDKW8EcC1SplKP1IUyaEli8Affodag9T1cQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.598.0': + resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-endpoint-discovery@3.598.0': + resolution: {integrity: sha512-TaFo3rfapVP0FiddH2zDyA5R5XNk2M+zMeUZaBRveYamSQ11F+fMGcedBgbOsv7yNESvaZvjlcw2K+cx3jOchA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-endpoint-discovery@3.620.0': + resolution: {integrity: sha512-T6kuydHBF4BPP5CVH53Fze7c2b9rqxWP88XrGtmNMXXdY4sXur1v/itGdS2l3gqRjxKo0LsmjmuQm9zL4vGneQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-expect-continue@3.387.0': + resolution: {integrity: sha512-w415a4tjQc6a7isq0AEDWFBC0HWUCHXEDjDl94UACxfMmS9bVabuf04t9CQ+nBBVs6HdiNdfdc/pBR2pRwx2Yg==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-expect-continue@3.598.0': + resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.387.0': + resolution: {integrity: sha512-QlH97rrKlcMyLG+2ps7+DtBHfPyRIpi7sD3y0iko2u3PGXk+PoLPK8wWyGql9sFopOYTl6/Jh2Rb1b6z6NbjEA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.598.0': + resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.387.0': + resolution: {integrity: sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-host-header@3.598.0': + resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.609.0': + resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.620.0': + resolution: {integrity: sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-location-constraint@3.387.0': + resolution: {integrity: sha512-Ipdry2V58CpDcWD0ZTz6yFtpTASEBxbuWdqUUYW7pOkZ/5GPGH8NhBky7M38iGqAO6FNysvWEVCUpIqNGkI1lw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-location-constraint@3.598.0': + resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.387.0': + resolution: {integrity: sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-logger@3.598.0': + resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.609.0': + resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.387.0': + resolution: {integrity: sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.598.0': + resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.609.0': + resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.620.0': + resolution: {integrity: sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.387.0': + resolution: {integrity: sha512-OIUBDzGhglI6KjXVwPLh7hRbrfCpSTwWRkbXbLrPgZZuzWMoJJ3q59RVkpLnAV9Mdkg6+YA6JTw4k4hcmJblVw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.598.0': + resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.622.0': + resolution: {integrity: sha512-tX9wZ2ALx5Ez4bkY+SvSj6DpNZ6TmY4zlsVsdgV95LZFLjNwqnZkKkS+uKnsIyLBiBp6g92JVQwnUEIp7ov2Zw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-sqs@3.598.0': + resolution: {integrity: sha512-BVHR5cKwxXTovezHHPzP7iSNZQdMp+Pn9l2zVfFzryE2Enahkg5oxgUcLD2jeFx14QxS1k7czIEIvKh991CWsg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-sts@3.387.0': + resolution: {integrity: sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-signing@3.387.0': + resolution: {integrity: sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-signing@3.598.0': + resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-ssec@3.387.0': + resolution: {integrity: sha512-Jtie1gqqcs7ZuYDlz/kuI3CKCXoCL5Ov/Gj5X8/XmwrQJEpuB6z0KY5H1qY0xo+jtAhC8nDPv0GnuLoOfn85hw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-ssec@3.598.0': + resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.387.0': + resolution: {integrity: sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/middleware-user-agent@3.598.0': + resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.609.0': + resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.620.0': + resolution: {integrity: sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.637.0': + resolution: {integrity: sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.645.0': + resolution: {integrity: sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.598.0': + resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.609.0': + resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.614.0': + resolution: {integrity: sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/s3-request-presigner@3.623.0': + resolution: {integrity: sha512-xdY7x4GQ3jVhkge0I8P2V/18p2unP3AD0m1zvacgFmxZ8tptjVpEg2fwR39gKv3pfri0DdfiPDrVONsPC2KlLw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.387.0': + resolution: {integrity: sha512-SGuUbEFi8BXYVv4M7Hc0488I7uZbTVBW19j/B7bnyfbKFrndBXM366s/mChx4iELtESQ61AAstyafx5nGj5tIg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@aws-sdk/signature-v4-crt': ^3.118.0 + peerDependenciesMeta: + '@aws-sdk/signature-v4-crt': + optional: true + + '@aws-sdk/signature-v4-multi-region@3.598.0': + resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.622.0': + resolution: {integrity: sha512-K7ddofVNzwTFRjmLZLfs/v+hiE9m5LguajHk8WULxXQgkcDI3nPgOfmMMGuslYohaQhRwW+ic+dzYlateLUudQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/token-providers@3.387.0': + resolution: {integrity: sha512-W9lPW6zR8yrfvDDLJnKCvHs2KwmydSo+1bG5i6WzFnY3aeOgPBJO2eDIJajZG8Q/L++ZwDaNDLL+ROnIMcg6GA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/token-providers@3.598.0': + resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.598.0 + + '@aws-sdk/token-providers@3.609.0': + resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.609.0 + + '@aws-sdk/token-providers@3.614.0': + resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.614.0 + + '@aws-sdk/types@3.387.0': + resolution: {integrity: sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/types@3.598.0': + resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/types@3.609.0': + resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-arn-parser@3.310.0': + resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/util-arn-parser@3.568.0': + resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-dynamodb@3.602.0': + resolution: {integrity: sha512-0QsRLE4cK0h9jseCbaBZpcLzBcOgPMdsEy4wIYvcXivHIdgt/JQxs329NF7EfWQU8h3PU5hRy3tiTBLuB4TmgQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.602.0 + + '@aws-sdk/util-dynamodb@3.637.0': + resolution: {integrity: sha512-C2q8HcGRiahtf46Mhaqydh1gofeksj7m74PJXHYKW+pKBMLPlpou1+w2o5QSpVEp0dSBtKw30eRVQzxhqg/ACA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.637.0 + + '@aws-sdk/util-dynamodb@3.648.0': + resolution: {integrity: sha512-w8cF5Ap8AL6VvA8bIbDNnrfpVvN3klsZRQ/QLVAhW1k3R3t9L+eKzoS3bBTVeyBlIh/eyXnSkQ8eduehS82FMw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.648.0 + + '@aws-sdk/util-dynamodb@3.658.1': + resolution: {integrity: sha512-lzlnis+35a2OhGZlVJvM3/30iIVoP2cIv5Bkw1F2nkM6Pr+1NOd3XvYhCY1Ud5zWtV6HUSptzessvUPqJTMfjQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.658.1 + + '@aws-sdk/util-endpoints@3.387.0': + resolution: {integrity: sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/util-endpoints@3.598.0': + resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.609.0': + resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.614.0': + resolution: {integrity: sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.637.0': + resolution: {integrity: sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.645.0': + resolution: {integrity: sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-format-url@3.609.0': + resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-locate-window@3.568.0': + resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-user-agent-browser@3.387.0': + resolution: {integrity: sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==} + + '@aws-sdk/util-user-agent-browser@3.598.0': + resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==} + + '@aws-sdk/util-user-agent-browser@3.609.0': + resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} + + '@aws-sdk/util-user-agent-node@3.387.0': + resolution: {integrity: sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==} + engines: {node: '>=14.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.598.0': + resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.609.0': + resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.614.0': + resolution: {integrity: sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-utf8-browser@3.259.0': + resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + + '@aws-sdk/xml-builder@3.310.0': + resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} + engines: {node: '>=14.0.0'} + + '@aws-sdk/xml-builder@3.598.0': + resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==} + engines: {node: '>=16.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.7': + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.7': + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.7': + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@es-joy/jsdoccomment@0.36.1': + resolution: {integrity: sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.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 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ewoudenberg/difflib@0.1.0': + resolution: {integrity: sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==} + + '@faker-js/faker@8.4.1': + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@liuli-util/fs-extra@0.1.0': + resolution: {integrity: sha512-eaAyDyMGT23QuRGbITVY3SOJff3G9ekAAyGqB9joAnTBmqvFN+9a1FazOdO70G6IUqgpKV451eBHYSRcOJ/FNQ==} + + '@mongodb-js/saslprep@1.1.7': + resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pagopa/eslint-config@3.0.0': + resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + + '@pagopa/interop-outbound-models@1.0.4-b': + resolution: {integrity: sha512-c1kxjIBCU0gqV2s3bLIdor+fIfvGLZgML2Y26dTdY3ppoXOhJBVTWKVBu5mq8nmZvZj0KnmK7tjeD6NUJjlywg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@protobuf-ts/plugin-framework@2.9.4': + resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + + '@protobuf-ts/plugin@2.9.4': + resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} + hasBin: true + + '@protobuf-ts/protoc@2.9.4': + resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} + hasBin: true + + '@protobuf-ts/runtime-rpc@2.9.4': + resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + + '@protobuf-ts/runtime@2.9.4': + resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + + '@puppeteer/browsers@2.2.3': + resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==} + engines: {node: '>=18'} + hasBin: true + + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.5.17': + resolution: {integrity: sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.6': + resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.1.6': + resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.0.5': + resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@sinonjs/fake-timers@11.3.1': + resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} + + '@sinonjs/samsam@8.0.2': + resolution: {integrity: sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==} + + '@sinonjs/text-encoding@0.7.3': + resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} + + '@smithy/abort-controller@2.2.0': + resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} + engines: {node: '>=14.0.0'} + + '@smithy/abort-controller@3.1.1': + resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} + engines: {node: '>=16.0.0'} + + '@smithy/chunked-blob-reader-native@2.2.0': + resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} + + '@smithy/chunked-blob-reader-native@3.0.0': + resolution: {integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==} + + '@smithy/chunked-blob-reader@2.2.0': + resolution: {integrity: sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==} + + '@smithy/chunked-blob-reader@3.0.0': + resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==} + + '@smithy/config-resolver@2.2.0': + resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} + engines: {node: '>=14.0.0'} + + '@smithy/config-resolver@3.0.4': + resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==} + engines: {node: '>=16.0.0'} + + '@smithy/config-resolver@3.0.5': + resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.2.4': + resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.4.0': + resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@2.3.0': + resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} + engines: {node: '>=14.0.0'} + + '@smithy/credential-provider-imds@3.1.3': + resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.2.0': + resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-codec@2.2.0': + resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} + + '@smithy/eventstream-codec@3.1.2': + resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==} + + '@smithy/eventstream-serde-browser@2.2.0': + resolution: {integrity: sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-browser@3.0.4': + resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-config-resolver@2.2.0': + resolution: {integrity: sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-config-resolver@3.0.3': + resolution: {integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-node@2.2.0': + resolution: {integrity: sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-node@3.0.4': + resolution: {integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-universal@2.2.0': + resolution: {integrity: sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==} + engines: {node: '>=14.0.0'} + + '@smithy/eventstream-serde-universal@3.0.4': + resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==} + engines: {node: '>=16.0.0'} + + '@smithy/fetch-http-handler@2.5.0': + resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} + + '@smithy/fetch-http-handler@3.2.0': + resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==} + + '@smithy/fetch-http-handler@3.2.4': + resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + + '@smithy/hash-blob-browser@2.2.0': + resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} + + '@smithy/hash-blob-browser@3.1.2': + resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==} + + '@smithy/hash-node@2.2.0': + resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} + engines: {node: '>=14.0.0'} + + '@smithy/hash-node@3.0.3': + resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-stream-node@2.2.0': + resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} + engines: {node: '>=14.0.0'} + + '@smithy/hash-stream-node@3.1.2': + resolution: {integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==} + engines: {node: '>=16.0.0'} + + '@smithy/invalid-dependency@2.2.0': + resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} + + '@smithy/invalid-dependency@3.0.3': + resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@3.0.0': + resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} + engines: {node: '>=16.0.0'} + + '@smithy/md5-js@2.2.0': + resolution: {integrity: sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==} + + '@smithy/md5-js@3.0.3': + resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==} + + '@smithy/middleware-content-length@2.2.0': + resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-content-length@3.0.3': + resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-content-length@3.0.5': + resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@2.5.1': + resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-endpoint@3.0.4': + resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.1.0': + resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@2.3.1': + resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-retry@3.0.15': + resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@3.0.7': + resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-serde@2.3.0': + resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-serde@3.0.3': + resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-stack@2.2.0': + resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} + engines: {node: '>=14.0.0'} + + '@smithy/middleware-stack@3.0.3': + resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@2.3.0': + resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} + engines: {node: '>=14.0.0'} + + '@smithy/node-config-provider@3.1.3': + resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@3.1.4': + resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@2.5.0': + resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} + engines: {node: '>=14.0.0'} + + '@smithy/node-http-handler@3.1.1': + resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.1.4': + resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@2.2.0': + resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} + engines: {node: '>=14.0.0'} + + '@smithy/property-provider@3.1.3': + resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@2.0.5': + resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} + engines: {node: '>=14.0.0'} + + '@smithy/protocol-http@3.3.0': + resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} + engines: {node: '>=14.0.0'} + + '@smithy/protocol-http@4.0.3': + resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.0': + resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@2.2.0': + resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} + engines: {node: '>=14.0.0'} + + '@smithy/querystring-builder@3.0.3': + resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-parser@2.2.0': + resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} + engines: {node: '>=14.0.0'} + + '@smithy/querystring-parser@3.0.3': + resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} + engines: {node: '>=16.0.0'} + + '@smithy/service-error-classification@2.1.5': + resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} + engines: {node: '>=14.0.0'} + + '@smithy/service-error-classification@3.0.3': + resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@2.4.0': + resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} + engines: {node: '>=14.0.0'} + + '@smithy/shared-ini-file-loader@3.1.3': + resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@3.1.4': + resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@2.3.0': + resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} + engines: {node: '>=14.0.0'} + + '@smithy/signature-v4@3.1.2': + resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@4.1.0': + resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@2.5.1': + resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} + engines: {node: '>=14.0.0'} + + '@smithy/smithy-client@3.1.5': + resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@3.2.0': + resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} + engines: {node: '>=16.0.0'} + + '@smithy/types@2.12.0': + resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} + engines: {node: '>=14.0.0'} + + '@smithy/types@3.3.0': + resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==} + engines: {node: '>=16.0.0'} + + '@smithy/url-parser@2.2.0': + resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} + + '@smithy/url-parser@3.0.3': + resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + + '@smithy/util-base64@2.3.0': + resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-base64@3.0.0': + resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-body-length-browser@2.2.0': + resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} + + '@smithy/util-body-length-browser@3.0.0': + resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + + '@smithy/util-body-length-node@2.3.0': + resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-body-length-node@3.0.0': + resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@3.0.0': + resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-config-provider@2.3.0': + resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} + engines: {node: '>=14.0.0'} + + '@smithy/util-config-provider@3.0.0': + resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-defaults-mode-browser@2.2.1': + resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.15': + resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.7': + resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@2.3.1': + resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.15': + resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.7': + resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-endpoints@2.0.4': + resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-endpoints@2.0.5': + resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-hex-encoding@2.2.0': + resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} + engines: {node: '>=14.0.0'} + + '@smithy/util-hex-encoding@3.0.0': + resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-middleware@2.2.0': + resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-middleware@3.0.3': + resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-retry@2.2.0': + resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} + engines: {node: '>= 14.0.0'} + + '@smithy/util-retry@3.0.3': + resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@2.2.0': + resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-stream@3.0.5': + resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.1.3': + resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-uri-escape@2.2.0': + resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-uri-escape@3.0.0': + resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@3.0.0': + resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-waiter@2.2.0': + resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-waiter@3.1.2': + resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==} + engines: {node: '>=16.0.0'} + + '@testcontainers/postgresql@10.9.0': + resolution: {integrity: sha512-Z3K/TFkl/PVE2v8A6yKqgF4pSFk9ilFG02yeGhPswUjmBlcig/rpVOjBQOkQ/yJCcQ/r2RrX3RR+7vr+UO4QlQ==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@tsconfig/node-lts@20.1.3': + resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tsconfig/strictest@2.0.5': + resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} + + '@types/adm-zip@0.5.5': + resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} + + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/buffers@0.1.31': + resolution: {integrity: sha512-wEZBb3o0Kh5RAj3V172vJCcxaCV8C2HJ7YLBBlG5Mwue0g4uRg5LWv8C6ap8MyFbXE6UbYEuvtHY7oTWAPeXEw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + + '@types/dockerode@3.3.29': + resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/html2json@1.0.1': + resolution: {integrity: sha512-D+cq6HcgfMdrbIpXzxsmJ9mBz9VlyagqaIB9OZVHGrOeyWbwjTLWR2qUGh1w/VZLtG4wy8mp7v4EpJQ1dYqWzw==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/json-diff@1.0.3': + resolution: {integrity: sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@9.0.6': + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + + '@types/lodash.isequal@4.5.8': + resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + + '@types/lodash@4.14.196': + resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} + + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/multer@1.4.11': + resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + + '@types/node@18.19.39': + resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + + '@types/node@20.14.6': + resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + + '@types/node@20.4.9': + resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==} + + '@types/nodemailer@6.4.15': + resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} + + '@types/nodemailer@6.4.9': + resolution: {integrity: sha512-XYG8Gv+sHjaOtUpiuytahMy2mM3rectgroNbs6R3djZEKmPNiIJwe9KqOJBGzKKnNZNKvnuvmugBgpq3w/S0ig==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/sinon@10.0.20': + resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} + + '@types/sinonjs__fake-timers@8.1.5': + resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + + '@types/ssh2-sftp-client@9.0.4': + resolution: {integrity: sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==} + + '@types/ssh2-streams@0.1.12': + resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.0': + resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + 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 + + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + 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 + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/type-utils@5.62.0': + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitest/expect@1.6.0': + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + + '@vitest/runner@1.6.0': + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + + '@vitest/snapshot@1.6.0': + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + + '@vitest/spy@1.6.0': + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + + '@vitest/utils@1.6.0': + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + + '@zodios/core@10.9.6': + resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} + peerDependencies: + axios: ^0.x || ^1.0.0 + zod: ^3.x + + '@zodios/express@10.6.1': + resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} + peerDependencies: + '@zodios/core': '>=10.4.4 <11.0.0' + express: 4.x + zod: ^3.x + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@2.0.1: + resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@2.7.0: + resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + adm-zip@0.5.15: + resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} + engines: {node: '>=12.0'} + + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-options@0.8.1: + resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} + engines: {node: '>=10.0.0'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + aws-msk-iam-sasl-signer-js@1.0.0: + resolution: {integrity: sha512-L0Jk0k2XNHMSGipJ8rRdTq51KrH/gwrfZ39iKY9BWHGOAv7EygsG4qJC7lIRsbu5/ZHB886Z3WsOsFxqR2R4XQ==} + engines: {node: '>=14.x'} + + aws-sdk-client-mock@4.0.1: + resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==} + + axios-logger@2.8.1: + resolution: {integrity: sha512-Bbl7XRR/Rkxg2Owv/kRgAZ/0qf8kMPLc08LtiUcGCWV5RmoI7vHr+eee6SUc8jRi2nE5KWShziCVh35C1SBEaw==} + + axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + + b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.4.2: + resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} + + bare-fs@2.3.1: + resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} + + bare-os@2.4.0: + resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} + + bare-path@2.1.3: + resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} + + bare-stream@2.1.3: + resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.1: + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} + engines: {node: '>=10.0.0'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001640: + resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} + + chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chromium-bidi@0.5.23: + resolution: {integrity: sha512-1o/gLU9wDqbN5nL2MtfjykjOuighGXc3/hnWueO1haiEoFgX8h5vbvcA4tgdQfjw1mkZ1OEF4x/+HVeqEX6NoA==} + peerDependencies: + devtools-protocol: '*' + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + 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==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comment-parser@1.3.1: + resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} + engines: {node: '>= 12.0.0'} + + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + connection-string@4.4.0: + resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} + engines: {node: '>=14'} + + console-assert@1.0.0: + resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + cpx2@7.0.1: + resolution: {integrity: sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==} + engines: {node: '>=18', npm: '>=10'} + hasBin: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + + create-eslint-index@1.0.0: + resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} + engines: {node: '>=4.0.0'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + csv-generate@4.4.1: + resolution: {integrity: sha512-O/einO0v4zPmXaOV+sYqGa02VkST4GP5GLpWBNHEouIU7pF3kpGf3D0kCCvX82ydIY4EKkOK+R8b1BYsRXravg==} + + csv-parse@5.5.6: + resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==} + + csv-stringify@6.5.1: + resolution: {integrity: sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==} + + csv@6.3.2: + resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} + engines: {node: '>= 0.1.90'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns-tz@3.1.3: + resolution: {integrity: sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==} + peerDependencies: + date-fns: ^3.0.0 + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + dateformat@3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + + debounce@2.1.0: + resolution: {integrity: sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==} + engines: {node: '>=18'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge-ts@4.3.0: + resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} + engines: {node: '>=12.4.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + depseek@0.4.1: + resolution: {integrity: sha512-YYfPPajzH9s2qnEva411VJzCMWtArBTfluI9USiKQ+T6xBWFh3C7yPxhaa1KVgJa17v9aRKc+LcRhgxS5/9mOA==} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + devtools-protocol@0.0.1299070: + resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + docker-compose@0.24.8: + resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} + engines: {node: '>= 6.0.0'} + + docker-modem@3.0.8: + resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} + engines: {node: '>= 8.0'} + + dockerode@3.3.5: + resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} + engines: {node: '>= 8.0'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv-flow@4.1.0: + resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} + engines: {node: '>= 12.0.0'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + drange@1.1.1: + resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} + engines: {node: '>=4'} + + dreamopt@0.8.0: + resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} + engines: {node: '>=0.4.0'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.4.818: + resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-ast-utils@1.1.0: + resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} + engines: {node: '>=4'} + + eslint-config-prettier@8.10.0: + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.8.1: + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + 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 + + eslint-plugin-extra-rules@0.0.0-development: + resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} + engines: {node: '> 0.10.*'} + + eslint-plugin-fp@2.3.0: + resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} + engines: {node: '>=4.0.0'} + peerDependencies: + eslint: '>=3' + + eslint-plugin-functional@4.4.1: + 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 + + eslint-plugin-import@2.29.1: + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsdoc@39.9.1: + resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + eslint-plugin-prefer-arrow@1.2.3: + resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} + peerDependencies: + eslint: '>=2.0.0' + + eslint-plugin-prettier@4.2.1: + 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 + + eslint-plugin-react@7.34.3: + resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-plugin-sonarjs@0.13.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 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + 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} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@3.0.0-alpha-1: + resolution: {integrity: sha512-HIv6P6qCt3ciLWri1KrO7EPigKPetBZwfCf0o9TuAxRBEPoUUisCepsZqvM76xRfQf2sheO4BC5R/w3UKhwx4w==} + engines: {node: '>=0.10.0'} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eval-estree-expression@2.0.0: + resolution: {integrity: sha512-e1VweC8biANiuLBY1kZSva3PpaiqaL79S9RR9ql9ngidCYM6tsY20xrUBEJsN63A1yVSmO92chPaBpIGBmGsPg==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + express@4.20.0: + resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} + engines: {node: '>= 0.10.0'} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-xml-parser@4.2.5: + resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} + hasBin: true + + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + + find-index@0.1.1: + resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + + get-uri@6.0.3: + resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob2base@0.0.12: + resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} + engines: {node: '>= 0.10'} + + glob@10.4.3: + resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} + engines: {node: '>=18'} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + handlebars@4.7.7: + resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} + engines: {node: '>=0.4.7'} + hasBin: true + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + 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'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + heap@0.2.7: + resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} + + html2json@1.0.2: + resolution: {integrity: sha512-tCdVt82U+/D1GCXFIoN5VfCzx767065EZJ5B8nStQUGSXU9PQ4L/0kFvF2of3Qsoe9HGaJni+lxOkqakfw0zpA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + 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'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + + jackspeak@3.4.1: + resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} + engines: {node: '>=18'} + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + jose@5.9.4: + resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + jsdoc-type-pratt-parser@3.1.0: + resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} + engines: {node: '>=12.0.0'} + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-diff@1.0.6: + resolution: {integrity: sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + just-extend@6.2.0: + resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jwks-rsa@3.1.0: + resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} + engines: {node: '>=14'} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + kafkajs@2.2.4: + resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} + engines: {node: '>=14.0.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + limiter@1.1.5: + resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isempty@4.4.0: + resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.zip@4.2.0: + resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lru-cache@10.4.0: + resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} + engines: {node: '>=18'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru-memoizer@2.3.0: + resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@4.0.4: + resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} + engines: {node: '>=16'} + hasBin: true + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mnemonist@0.38.3: + resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.7.0: + resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multer@1.4.5-lts.1: + resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} + engines: {node: '>= 6.0.0'} + + nan@2.20.0: + resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + nise@5.1.9: + resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + nodemailer@6.9.14: + resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==} + engines: {node: '>=6.0.0'} + + nodemailer@6.9.9: + resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} + engines: {node: '>=6.0.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + obliterator@1.6.1: + resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + openapi-zod-client@1.18.1: + resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} + hasBin: true + + openapi3-ts@3.1.0: + resolution: {integrity: sha512-1qKTvCCVoV0rkwUh1zq5o8QyghmwYPuhdvtjv1rFjuOnJToXhQyF8eGjNETQ8QmGjr9Jz/tkAKLITIl2s7dw3A==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@6.0.0: + resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} + engines: {node: '>=16'} + + pac-proxy-agent@7.0.2: + resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pastable@2.2.1: + resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} + engines: {node: '>=14.x'} + peerDependencies: + react: '>=17' + xstate: '>=4.32.1' + peerDependenciesMeta: + react: + optional: true + xstate: + optional: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-minify@1.6.4: + resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} + engines: {node: '>=14.0.0'} + + pg-pool@3.6.2: + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + + pg-promise@11.8.0: + resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} + engines: {node: '>=14.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.11.5: + resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@2.3.0: + resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} + engines: {node: '>=14'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.4.0: + resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + puppeteer-core@22.11.2: + resolution: {integrity: sha512-vQo+YDuePyvj+92Z9cdtxi/HalKf+k/R4tE80nGtQqJRNqU81eHaHkbVfnLszdaLlvwFF5tipnnSCzqWlEddtw==} + engines: {node: '>=18'} + + puppeteer@22.11.2: + resolution: {integrity: sha512-8fjdQSgW0sq7471ftca24J7sXK+jXZ7OW7Gx+NEBFNyXrcTiBfukEI46gNq6hiMhbLEDT30NeylK/1ZoPdlKSA==} + engines: {node: '>=18'} + hasBin: true + + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + + qs@6.12.3: + resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} + engines: {node: '>=0.6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + + quote@0.4.0: + resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} + + randexp@0.5.3: + resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} + engines: {node: '>=4'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rate-limiter-flexible@5.0.3: + resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + redis@4.6.15: + resolution: {integrity: sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + req-all@0.1.0: + resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} + engines: {node: '>=4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.0: + resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sinon@16.1.3: + resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.4: + resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + engines: {node: '>= 14'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + + spex@3.3.0: + resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} + engines: {node: '>=10.0.0'} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2-sftp-client@9.1.0: + resolution: {integrity: sha512-Hzdr9OE6GxZjcmyM9tgBSIFVyrHAp9c6U2Y4yBkmYOHoQvZ7pIm27dmltvcmRfxcWiIcg8HBvG5iAikDf+ZuzQ==} + engines: {node: '>=10.24.1'} + + ssh2@1.15.0: + resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} + engines: {node: '>=10.16.0'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-transform@3.3.2: + resolution: {integrity: sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + streamx@2.18.0: + resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + + subarg@1.0.0: + resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tanu@0.1.13: + resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} + + tar-fs@2.0.1: + resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + + tar-fs@3.0.5: + resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + + tar-fs@3.0.6: + resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + testcontainers@10.9.0: + resolution: {integrity: sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==} + + text-decoder@1.1.1: + resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + + tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-node@10.9.2: + 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 + + ts-pattern@5.2.0: + resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsc-esm-fix@2.20.27: + resolution: {integrity: sha512-bfoSY29XN4yRvXgfxc4rtKQPe9Xx02BahWSZ3D4GgBXIWSE+TJ/BXGSrpUIBkrsKIUQv2zA3qiwJVFnUV59Xdw==} + engines: {node: '>=16.0.0'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tsutils@3.21.0: + 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' + + tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + engines: {node: '>=18.0.0'} + hasBin: true + + turbo-darwin-64@2.0.4: + resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.0.4: + resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.0.4: + resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.0.4: + resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.0.4: + resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.0.4: + resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} + cpu: [arm64] + os: [win32] + + turbo@2.0.4: + resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} + hasBin: true + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typescript@3.9.10: + resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + uglify-js@3.18.0: + resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@1.6.0: + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + 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 + + 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 + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whence@2.0.1: + resolution: {integrity: sha512-VtcCE1Pe3BKofF/k+P5xcpuoqQ0f1NJY6TmdUw5kInl9/pEr1ZEFD9+ZOUicf52tvpTbhMS93aWXriu2IQYTTw==} + engines: {node: '>=14'} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + + winston@3.13.0: + resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + + zod-validation-error@3.3.0: + resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@anatine/zod-mock@3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8)': + dependencies: + '@faker-js/faker': 8.4.1 + randexp: 0.5.3 + zod: 3.23.8 + + '@apidevtools/json-schema-ref-parser@9.0.6': + dependencies: + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + js-yaml: 3.14.1 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 9.0.6 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + ajv: 8.16.0 + ajv-draft-04: 1.0.0(ajv@8.16.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + + '@aws-crypto/crc32@3.0.0': + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.3 + + '@aws-crypto/crc32c@3.0.0': + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/crc32c@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.3 + + '@aws-crypto/ie11-detection@3.0.0': + dependencies: + tslib: 1.14.1 + + '@aws-crypto/sha1-browser@3.0.0': + dependencies: + '@aws-crypto/ie11-detection': 3.0.0 + '@aws-crypto/supports-web-crypto': 3.0.0 + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/sha1-browser@5.2.0': + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.3 + + '@aws-crypto/sha256-browser@3.0.0': + dependencies: + '@aws-crypto/ie11-detection': 3.0.0 + '@aws-crypto/sha256-js': 3.0.0 + '@aws-crypto/supports-web-crypto': 3.0.0 + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-locate-window': 3.568.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.3 + + '@aws-crypto/sha256-js@3.0.0': + dependencies: + '@aws-crypto/util': 3.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/sha256-js@4.0.0': + dependencies: + '@aws-crypto/util': 4.0.0 + '@aws-sdk/types': 3.609.0 + tslib: 1.14.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.609.0 + tslib: 2.6.3 + + '@aws-crypto/supports-web-crypto@3.0.0': + dependencies: + tslib: 1.14.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.6.3 + + '@aws-crypto/util@3.0.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/util@4.0.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-utf8-browser': 3.259.0 + tslib: 1.14.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.6.3 + + '@aws-sdk/client-cognito-identity@3.609.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/middleware-host-header': 3.609.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.609.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.609.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-dynamodb@3.602.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/core': 3.598.0 + '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/middleware-endpoint-discovery': 3.598.0 + '@aws-sdk/middleware-host-header': 3.598.0 + '@aws-sdk/middleware-logger': 3.598.0 + '@aws-sdk/middleware-recursion-detection': 3.598.0 + '@aws-sdk/middleware-user-agent': 3.598.0 + '@aws-sdk/region-config-resolver': 3.598.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 + '@aws-sdk/util-user-agent-browser': 3.598.0 + '@aws-sdk/util-user-agent-node': 3.598.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-utf8': 3.0.0 '@smithy/util-waiter': 3.1.2 tslib: 2.6.3 uuid: 9.0.1 transitivePeerDependencies: - aws-crt - /@aws-sdk/client-dynamodb@3.648.0: - resolution: {integrity: sha512-61yU6wQRlwOhD0mfJS/N8SYmv9hxkVYGKsXqSJ5PNNnySutoNof7cmX8cTuijpTQqLL9sKPfvPMlJCv7/M1AiA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-dynamodb@3.637.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sts': 3.637.0 + '@aws-sdk/core': 3.635.0 + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/middleware-endpoint-discovery': 3.620.0 + '@aws-sdk/middleware-host-header': 3.620.0 + '@aws-sdk/middleware-logger': 3.609.0 + '@aws-sdk/middleware-recursion-detection': 3.620.0 + '@aws-sdk/middleware-user-agent': 3.637.0 + '@aws-sdk/region-config-resolver': 3.614.0 + '@aws-sdk/types': 3.609.0 + '@aws-sdk/util-endpoints': 3.637.0 + '@aws-sdk/util-user-agent-browser': 3.609.0 + '@aws-sdk/util-user-agent-node': 3.614.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 + '@smithy/hash-node': 3.0.3 + '@smithy/invalid-dependency': 3.0.3 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.3 + '@smithy/util-utf8': 3.0.0 + '@smithy/util-waiter': 3.1.2 + tslib: 2.6.3 + uuid: 9.0.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-dynamodb@3.648.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/middleware-endpoint-discovery': 3.620.0 '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 @@ -3048,11 +7906,8 @@ packages: uuid: 9.0.1 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-kms@3.600.0: - resolution: {integrity: sha512-m1o8aiVrVjExw6O+8JszXV3hr8sCyXKOLq1WCwWJqYF6Uf4vCf8iTYISQB3skbKUnBJm4SxVA82iViGAtWB7JA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-kms@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3097,11 +7952,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-s3@3.387.0: - resolution: {integrity: sha512-TX42MDfXnIy/U6f8XjCTR1Ezg1125Sv5k9kdKZJ0kkKcb/81N0+RfTqsR+Kpn/AbLjtvSUWrFIdc1o//GsfZAQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-s3@3.387.0': dependencies: '@aws-crypto/sha1-browser': 3.0.0 '@aws-crypto/sha256-browser': 3.0.0 @@ -3160,11 +8012,8 @@ packages: transitivePeerDependencies: - '@aws-sdk/signature-v4-crt' - aws-crt - dev: false - /@aws-sdk/client-s3@3.600.0: - resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-s3@3.600.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 @@ -3226,18 +8075,15 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sesv2@3.620.1: - resolution: {integrity: sha512-pu/CbRQuxCA0EmDqyAktc77pjbmeWuQao6aLDBuXLDbccwKgRECLCTscqUcnqtfFhPkiOAim9LdczqEXLNIpcA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sesv2@3.620.1': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3276,9 +8122,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sqs@3.600.0: - resolution: {integrity: sha512-GgjEiWbGbiHGU3yZcCr1hXfaq/B/3ncYclqLEbbxrWkQOdri3qfl278h+Qn0/DQ8On0kj1UUxld87TVIkYfG8w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sqs@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3325,11 +8169,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3374,19 +8215,14 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sts' - aws-crt - dev: false - /@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -3425,66 +8261,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1): - resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 + '@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.620.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - /@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3523,17 +8306,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.637.0 + '@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3572,66 +8351,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.645.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - /@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 + '@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -3670,9 +8396,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.387.0: - resolution: {integrity: sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sso@3.387.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 @@ -3709,11 +8433,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso@3.598.0: - resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.598.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3755,11 +8476,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso@3.609.0: - resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3802,9 +8520,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.620.1: - resolution: {integrity: sha512-4Ox0BSs+atrAhLvjNHN2uiYvSTdpMv//IS4l4XRoQG0cJKIPLs3OU3PL5H0X1NfZehz9/8FTWl5Lv81uw4j1eA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.620.1': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3847,9 +8563,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.637.0: - resolution: {integrity: sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.637.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3892,9 +8606,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.645.0: - resolution: {integrity: sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.645.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3937,9 +8649,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.387.0: - resolution: {integrity: sha512-RPOME0gpAViheH6xHyMg/XkE1G/fs6dgKK/NqlBZDjwMsSTPc8CmItEC6FOsCaLJktif0tD/u9m2uaQ4Lb1nVw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sts@3.387.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 @@ -3980,11 +8690,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sts@3.600.0: - resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -4028,17 +8735,14 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sts@3.609.0: - resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -4048,28 +8752,28 @@ packages: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.4 - '@smithy/core': 2.2.4 - '@smithy/fetch-http-handler': 3.2.0 + '@smithy/config-resolver': 3.0.5 + '@smithy/core': 2.4.0 + '@smithy/fetch-http-handler': 3.2.4 '@smithy/hash-node': 3.0.3 '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.3 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 + '@smithy/middleware-content-length': 3.0.5 + '@smithy/middleware-endpoint': 3.1.0 + '@smithy/middleware-retry': 3.0.15 '@smithy/middleware-serde': 3.0.3 '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.1 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 + '@smithy/node-config-provider': 3.1.4 + '@smithy/node-http-handler': 3.1.4 + '@smithy/protocol-http': 4.1.0 + '@smithy/smithy-client': 3.2.0 '@smithy/types': 3.3.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.7 - '@smithy/util-defaults-mode-node': 3.0.7 - '@smithy/util-endpoints': 2.0.4 + '@smithy/util-defaults-mode-browser': 3.0.15 + '@smithy/util-defaults-mode-node': 3.0.15 + '@smithy/util-endpoints': 2.0.5 '@smithy/util-middleware': 3.0.3 '@smithy/util-retry': 3.0.3 '@smithy/util-utf8': 3.0.0 @@ -4077,15 +8781,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.620.1: - resolution: {integrity: sha512-d+ECGFDg0IsDdmfKU2O0VeMYKZcmbfBaA9HkZnZ39wu1BlXGI73xJe8cfmzbobvu+Ly+bAfHdLCpgIY+pD4D7g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.620.1': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -4124,15 +8826,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.637.0: - resolution: {integrity: sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.637.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -4171,15 +8871,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.645.0: - resolution: {integrity: sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.645.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/middleware-host-header': 3.620.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.620.0 @@ -4218,9 +8916,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/core@3.598.0: - resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.598.0': dependencies: '@smithy/core': 2.4.0 '@smithy/protocol-http': 4.1.0 @@ -4229,11 +8925,8 @@ packages: '@smithy/types': 3.3.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 - dev: false - /@aws-sdk/core@3.609.0: - resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.609.0': dependencies: '@smithy/core': 2.4.0 '@smithy/protocol-http': 4.1.0 @@ -4243,9 +8936,7 @@ packages: fast-xml-parser: 4.2.5 tslib: 2.6.3 - /@aws-sdk/core@3.620.1: - resolution: {integrity: sha512-6Ejce93dDlDnovl6oYtxj3I/SJMOQoFdmmtM4+4W/cgMWH+l00T5aszVxDLjjPfu3Ryt7dNhrXaYeK2Ue1ZBmg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.620.1': dependencies: '@smithy/core': 2.4.0 '@smithy/node-config-provider': 3.1.4 @@ -4257,9 +8948,7 @@ packages: fast-xml-parser: 4.2.5 tslib: 2.6.3 - /@aws-sdk/core@3.635.0: - resolution: {integrity: sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.635.0': dependencies: '@smithy/core': 2.4.0 '@smithy/node-config-provider': 3.1.4 @@ -4272,9 +8961,7 @@ packages: fast-xml-parser: 4.4.1 tslib: 2.6.3 - /@aws-sdk/credential-provider-cognito-identity@3.609.0: - resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-cognito-identity@3.609.0': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/types': 3.609.0 @@ -4283,49 +8970,36 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-env@3.387.0: - resolution: {integrity: sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-env@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-env@3.598.0: - resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-env@3.609.0: - resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-env@3.620.1: - resolution: {integrity: sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.598.0: - resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4336,11 +9010,8 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-http@3.609.0: - resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4352,9 +9023,7 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.620.0: - resolution: {integrity: sha512-BI2BdrSKDmB/2ouB/NJR0PT0x/+5fmoF6XOE78hFBb4F5w/yynGgcJY936dF+oREfpME6ehjB2b0okGg78Scpw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4366,9 +9035,7 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.635.0: - resolution: {integrity: sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.635.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.4 @@ -4380,9 +9047,7 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@aws-sdk/credential-provider-ini@3.387.0: - resolution: {integrity: sha512-DS2Jg5E4Hd9fhJqTVNBG3SEwLwcyguPDcXSVCDz5pEHlYFM1U4x9b7aAbutzZujTH99MZ6Gua8kAotB/qjEjtw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-ini@3.387.0': dependencies: '@aws-sdk/credential-provider-env': 3.387.0 '@aws-sdk/credential-provider-process': 3.387.0 @@ -4396,13 +9061,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 + '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/credential-provider-env': 3.598.0 @@ -4419,41 +9079,14 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - - /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4464,20 +9097,15 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-ini@3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-m9jwigMPRlRRhoPxCQZMOwQUd6imEJbksF6tSMYNae76DIvrCi4z2Jhp6RJ9Mij8cnewUZCAmvu2FlK9+n9M7A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 + '@aws-sdk/credential-provider-ini@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1)': dependencies: - '@aws-sdk/client-sts': 3.645.0 + '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.620.0 '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 @@ -4488,17 +9116,13 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-ini@3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.637.0 + '@aws-sdk/credential-provider-ini@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0) + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4510,39 +9134,13 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 - dependencies: - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - - /@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 + '@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4554,9 +9152,7 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-node@3.387.0: - resolution: {integrity: sha512-NviQ0EqigPWwX4avKheRzE2R4YPzO6qzdyxKZUookr+uTWYroQ4ePZbHK1/BD8LlqKKBlttX/d3ENXjynU4clA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-node@3.387.0': dependencies: '@aws-sdk/credential-provider-env': 3.387.0 '@aws-sdk/credential-provider-ini': 3.387.0 @@ -4571,11 +9167,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 @@ -4593,38 +9186,14 @@ packages: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - dev: false - - /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-sdk/credential-provider-env': 3.609.0 - '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4636,18 +9205,15 @@ packages: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - dev: false - /@aws-sdk/credential-provider-node@3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-KaprIJW2azM+oTIHi7S1ayJ3oQqoFwpMBWFpZM1nvSzaPucrZIUmX2m4uVrMM4LfXsfUsgMkrme2rBI1fGAjCg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1)': dependencies: '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.620.0 - '@aws-sdk/credential-provider-ini': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-ini': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 @@ -4659,15 +9225,13 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-node@3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0)(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0) + '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4680,36 +9244,13 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - - /@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/credential-provider-env': 3.620.1 '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.0 @@ -4722,31 +9263,23 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-process@3.387.0: - resolution: {integrity: sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-process@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-process@3.598.0: - resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-process@3.609.0: - resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 @@ -4754,9 +9287,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-process@3.620.1: - resolution: {integrity: sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 @@ -4764,9 +9295,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-sso@3.387.0: - resolution: {integrity: sha512-sQgrEyTSrwLe8zgjP9VEUDz3dtGXSCc4k00bCwODbzdOWCA1nz9oF2tFmgjFsb1Q80pae01Pe50Esix5z2eHsQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-sso@3.387.0': dependencies: '@aws-sdk/client-sso': 3.387.0 '@aws-sdk/token-providers': 3.387.0 @@ -4777,11 +9306,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0): - resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-sdk/client-sso': 3.598.0 '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) @@ -4793,29 +9319,11 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - - /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): - resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4824,14 +9332,11 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-sso@3.620.1(@aws-sdk/client-sso-oidc@3.620.1): - resolution: {integrity: sha512-cFU8e6ctdkWR8BRCnHFzs37N+ilbHf1OT2EeMjt1ZDE9FgTD5L5BTgVWDxnPmyQnEoBs1p4PyNPHkpHY5EmswQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': dependencies: '@aws-sdk/client-sso': 3.620.1 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1) + '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4841,12 +9346,10 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-sso@3.637.0(@aws-sdk/client-sso-oidc@3.637.0): - resolution: {integrity: sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': dependencies: '@aws-sdk/client-sso': 3.637.0 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0) + '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4856,12 +9359,10 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-sso@3.645.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': dependencies: '@aws-sdk/client-sso': 3.645.0 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 @@ -4871,34 +9372,22 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-web-identity@3.387.0: - resolution: {integrity: sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/credential-provider-web-identity@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0): - resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 + '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/types': 3.609.0 @@ -4906,35 +9395,15 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 - dependencies: - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - - /@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.620.1)': dependencies: - '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0): - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/types': 3.609.0 @@ -4942,11 +9411,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0): - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/types': 3.609.0 @@ -4954,9 +9419,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/client-sso': 3.609.0 @@ -4964,10 +9427,10 @@ packages: '@aws-sdk/credential-provider-cognito-identity': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.1.3 @@ -4977,18 +9440,13 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/endpoint-cache@3.572.0: - resolution: {integrity: sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/endpoint-cache@3.572.0': dependencies: mnemonist: 0.38.3 tslib: 2.6.3 - /@aws-sdk/middleware-bucket-endpoint@3.387.0: - resolution: {integrity: sha512-o7Dsq0YTUHFcKXD6+30/fXv/Wzdxqz9WonhCu3ZFPwTDLZgOM4QDDKW8EcC1SplKP1IUyaEli8Affodag9T1cQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@aws-sdk/util-arn-parser': 3.310.0 @@ -4996,11 +9454,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-config-provider': 2.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-bucket-endpoint@3.598.0: - resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 @@ -5009,11 +9464,8 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-endpoint-discovery@3.598.0: - resolution: {integrity: sha512-TaFo3rfapVP0FiddH2zDyA5R5XNk2M+zMeUZaBRveYamSQ11F+fMGcedBgbOsv7yNESvaZvjlcw2K+cx3jOchA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-endpoint-discovery@3.598.0': dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.598.0 @@ -5021,11 +9473,8 @@ packages: '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-endpoint-discovery@3.620.0: - resolution: {integrity: sha512-T6kuydHBF4BPP5CVH53Fze7c2b9rqxWP88XrGtmNMXXdY4sXur1v/itGdS2l3gqRjxKo0LsmjmuQm9zL4vGneQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-endpoint-discovery@3.620.0': dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.609.0 @@ -5034,29 +9483,21 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-expect-continue@3.387.0: - resolution: {integrity: sha512-w415a4tjQc6a7isq0AEDWFBC0HWUCHXEDjDl94UACxfMmS9bVabuf04t9CQ+nBBVs6HdiNdfdc/pBR2pRwx2Yg==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-expect-continue@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-expect-continue@3.598.0: - resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-expect-continue@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/protocol-http': 4.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-flexible-checksums@3.387.0: - resolution: {integrity: sha512-QlH97rrKlcMyLG+2ps7+DtBHfPyRIpi7sD3y0iko2u3PGXk+PoLPK8wWyGql9sFopOYTl6/Jh2Rb1b6z6NbjEA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.387.0': dependencies: '@aws-crypto/crc32': 3.0.0 '@aws-crypto/crc32c': 3.0.0 @@ -5066,11 +9507,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-flexible-checksums@3.598.0: - resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.598.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 @@ -5080,142 +9518,102 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.387.0: - resolution: {integrity: sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-host-header@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.598.0: - resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.609.0: - resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-host-header@3.620.0: - resolution: {integrity: sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-location-constraint@3.387.0: - resolution: {integrity: sha512-Ipdry2V58CpDcWD0ZTz6yFtpTASEBxbuWdqUUYW7pOkZ/5GPGH8NhBky7M38iGqAO6FNysvWEVCUpIqNGkI1lw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-location-constraint@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-location-constraint@3.598.0: - resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-location-constraint@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.387.0: - resolution: {integrity: sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-logger@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.598.0: - resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.609.0: - resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-recursion-detection@3.387.0: - resolution: {integrity: sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-recursion-detection@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-recursion-detection@3.598.0: - resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-recursion-detection@3.609.0: - resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-recursion-detection@3.620.0: - resolution: {integrity: sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-sdk-s3@3.387.0: - resolution: {integrity: sha512-OIUBDzGhglI6KjXVwPLh7hRbrfCpSTwWRkbXbLrPgZZuzWMoJJ3q59RVkpLnAV9Mdkg6+YA6JTw4k4hcmJblVw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-sdk-s3@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@aws-sdk/util-arn-parser': 3.310.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-s3@3.598.0: - resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 @@ -5226,11 +9624,8 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-s3@3.622.0: - resolution: {integrity: sha512-tX9wZ2ALx5Ez4bkY+SvSj6DpNZ6TmY4zlsVsdgV95LZFLjNwqnZkKkS+uKnsIyLBiBp6g92JVQwnUEIp7ov2Zw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.622.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-arn-parser': 3.568.0 @@ -5243,11 +9638,8 @@ packages: '@smithy/util-stream': 3.1.3 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-sqs@3.598.0: - resolution: {integrity: sha512-BVHR5cKwxXTovezHHPzP7iSNZQdMp+Pn9l2zVfFzryE2Enahkg5oxgUcLD2jeFx14QxS1k7czIEIvKh991CWsg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-sqs@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/smithy-client': 3.1.5 @@ -5255,21 +9647,15 @@ packages: '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-sts@3.387.0: - resolution: {integrity: sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-sdk-sts@3.387.0': dependencies: '@aws-sdk/middleware-signing': 3.387.0 '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-signing@3.387.0: - resolution: {integrity: sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-signing@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 @@ -5278,11 +9664,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-middleware': 2.2.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-signing@3.598.0: - resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-signing@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 @@ -5291,51 +9674,36 @@ packages: '@smithy/types': 3.3.0 '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-ssec@3.387.0: - resolution: {integrity: sha512-Jtie1gqqcs7ZuYDlz/kuI3CKCXoCL5Ov/Gj5X8/XmwrQJEpuB6z0KY5H1qY0xo+jtAhC8nDPv0GnuLoOfn85hw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-ssec@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-ssec@3.598.0: - resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-ssec@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.387.0: - resolution: {integrity: sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-user-agent@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@aws-sdk/util-endpoints': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.598.0: - resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-endpoints': 3.598.0 '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.609.0: - resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.609.0 @@ -5343,9 +9711,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-user-agent@3.620.0: - resolution: {integrity: sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.614.0 @@ -5353,9 +9719,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-user-agent@3.637.0: - resolution: {integrity: sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.637.0 @@ -5363,9 +9727,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/middleware-user-agent@3.645.0: - resolution: {integrity: sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.645.0 @@ -5373,9 +9735,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/region-config-resolver@3.598.0: - resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/node-config-provider': 3.1.4 @@ -5383,11 +9743,8 @@ packages: '@smithy/util-config-provider': 3.0.0 '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - dev: false - /@aws-sdk/region-config-resolver@3.609.0: - resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 @@ -5396,9 +9753,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@aws-sdk/region-config-resolver@3.614.0: - resolution: {integrity: sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 @@ -5407,9 +9762,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@aws-sdk/s3-request-presigner@3.623.0: - resolution: {integrity: sha512-xdY7x4GQ3jVhkge0I8P2V/18p2unP3AD0m1zvacgFmxZ8tptjVpEg2fwR39gKv3pfri0DdfiPDrVONsPC2KlLw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/s3-request-presigner@3.623.0': dependencies: '@aws-sdk/signature-v4-multi-region': 3.622.0 '@aws-sdk/types': 3.609.0 @@ -5419,27 +9772,16 @@ packages: '@smithy/smithy-client': 3.2.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.387.0: - resolution: {integrity: sha512-SGuUbEFi8BXYVv4M7Hc0488I7uZbTVBW19j/B7bnyfbKFrndBXM366s/mChx4iELtESQ61AAstyafx5nGj5tIg==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@aws-sdk/signature-v4-crt': ^3.118.0 - peerDependenciesMeta: - '@aws-sdk/signature-v4-crt': - optional: true + '@aws-sdk/signature-v4-multi-region@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/protocol-http': 2.0.5 '@smithy/signature-v4': 2.3.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.598.0: - resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.598.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.598.0 '@aws-sdk/types': 3.598.0 @@ -5447,11 +9789,8 @@ packages: '@smithy/signature-v4': 3.1.2 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.622.0: - resolution: {integrity: sha512-K7ddofVNzwTFRjmLZLfs/v+hiE9m5LguajHk8WULxXQgkcDI3nPgOfmMMGuslYohaQhRwW+ic+dzYlateLUudQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.622.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.622.0 '@aws-sdk/types': 3.609.0 @@ -5459,24 +9798,16 @@ packages: '@smithy/signature-v4': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.387.0: - resolution: {integrity: sha512-W9lPW6zR8yrfvDDLJnKCvHs2KwmydSo+1bG5i6WzFnY3aeOgPBJO2eDIJajZG8Q/L++ZwDaNDLL+ROnIMcg6GA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/token-providers@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0): - resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.598.0 + '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 @@ -5484,13 +9815,8 @@ packages: '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): - resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.609.0 + '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 @@ -5499,38 +9825,16 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.609.0 - dependencies: - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - tslib: 2.6.3 - dev: false - - /@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1): - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 + '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': dependencies: - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.645.0) + '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0): - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 + '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 @@ -5539,285 +9843,179 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 + '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/types@3.387.0: - resolution: {integrity: sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==} - engines: {node: '>=14.0.0'} + '@aws-sdk/types@3.387.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/types@3.598.0: - resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.598.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/types@3.609.0: - resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.609.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/util-arn-parser@3.310.0: - resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/util-arn-parser@3.310.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/util-arn-parser@3.568.0: - resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.568.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.602.0(@aws-sdk/client-dynamodb@3.602.0): - resolution: {integrity: sha512-0QsRLE4cK0h9jseCbaBZpcLzBcOgPMdsEy4wIYvcXivHIdgt/JQxs329NF7EfWQU8h3PU5hRy3tiTBLuB4TmgQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.602.0 + '@aws-sdk/util-dynamodb@3.602.0(@aws-sdk/client-dynamodb@3.602.0)': dependencies: '@aws-sdk/client-dynamodb': 3.602.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.637.0(@aws-sdk/client-dynamodb@3.637.0): - resolution: {integrity: sha512-C2q8HcGRiahtf46Mhaqydh1gofeksj7m74PJXHYKW+pKBMLPlpou1+w2o5QSpVEp0dSBtKw30eRVQzxhqg/ACA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.637.0 + '@aws-sdk/util-dynamodb@3.637.0(@aws-sdk/client-dynamodb@3.637.0)': dependencies: '@aws-sdk/client-dynamodb': 3.637.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.648.0(@aws-sdk/client-dynamodb@3.648.0): - resolution: {integrity: sha512-w8cF5Ap8AL6VvA8bIbDNnrfpVvN3klsZRQ/QLVAhW1k3R3t9L+eKzoS3bBTVeyBlIh/eyXnSkQ8eduehS82FMw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.648.0 + '@aws-sdk/util-dynamodb@3.648.0(@aws-sdk/client-dynamodb@3.648.0)': dependencies: '@aws-sdk/client-dynamodb': 3.648.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.658.1(@aws-sdk/client-dynamodb@3.637.0): - resolution: {integrity: sha512-lzlnis+35a2OhGZlVJvM3/30iIVoP2cIv5Bkw1F2nkM6Pr+1NOd3XvYhCY1Ud5zWtV6HUSptzessvUPqJTMfjQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.658.1 + '@aws-sdk/util-dynamodb@3.658.1(@aws-sdk/client-dynamodb@3.637.0)': dependencies: '@aws-sdk/client-dynamodb': 3.637.0 tslib: 2.6.3 - dev: true - /@aws-sdk/util-endpoints@3.387.0: - resolution: {integrity: sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==} - engines: {node: '>=14.0.0'} + '@aws-sdk/util-endpoints@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-endpoints@3.598.0: - resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - dev: false - /@aws-sdk/util-endpoints@3.609.0: - resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.614.0: - resolution: {integrity: sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.637.0: - resolution: {integrity: sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.645.0: - resolution: {integrity: sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 '@smithy/util-endpoints': 2.0.5 tslib: 2.6.3 - /@aws-sdk/util-format-url@3.609.0: - resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-format-url@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/querystring-builder': 3.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-locate-window@3.568.0: - resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-locate-window@3.568.0': dependencies: tslib: 2.6.3 - /@aws-sdk/util-user-agent-browser@3.387.0: - resolution: {integrity: sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==} + '@aws-sdk/util-user-agent-browser@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/types': 2.12.0 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-browser@3.598.0: - resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==} + '@aws-sdk/util-user-agent-browser@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/types': 3.3.0 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-browser@3.609.0: - resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} + '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.3.0 bowser: 2.11.0 tslib: 2.6.3 - /@aws-sdk/util-user-agent-node@3.387.0: - resolution: {integrity: sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==} - engines: {node: '>=14.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.387.0': dependencies: '@aws-sdk/types': 3.387.0 '@smithy/node-config-provider': 2.3.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-node@3.598.0: - resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-node@3.609.0: - resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/util-user-agent-node@3.614.0: - resolution: {integrity: sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@aws-sdk/util-utf8-browser@3.259.0: - resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + '@aws-sdk/util-utf8-browser@3.259.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/xml-builder@3.310.0: - resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} - engines: {node: '>=14.0.0'} + '@aws-sdk/xml-builder@3.310.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/xml-builder@3.598.0: - resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/xml-builder@3.598.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@babel/code-frame@7.24.7: - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - /@babel/compat-data@7.24.7: - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} - engines: {node: '>=6.9.0'} + '@babel/compat-data@7.24.7': {} - /@babel/core@7.24.7: - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} - engines: {node: '>=6.9.0'} + '@babel/core@7.24.7': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.7 @@ -5837,18 +10035,14 @@ packages: transitivePeerDependencies: - supports-color - /@babel/generator@7.24.7: - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} - engines: {node: '>=6.9.0'} + '@babel/generator@7.24.7': dependencies: '@babel/types': 7.24.7 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - /@babel/helper-compilation-targets@7.24.7: - resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} - engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.24.7': dependencies: '@babel/compat-data': 7.24.7 '@babel/helper-validator-option': 7.24.7 @@ -5856,39 +10050,27 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-environment-visitor@7.24.7: - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-environment-visitor@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-function-name@7.24.7: - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} + '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - /@babel/helper-hoist-variables@7.24.7: - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-hoist-variables@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-module-imports@7.24.7: - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - /@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7): - resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-environment-visitor': 7.24.7 @@ -5899,67 +10081,46 @@ packages: transitivePeerDependencies: - supports-color - /@babel/helper-simple-access@7.24.7: - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} + '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - /@babel/helper-split-export-declaration@7.24.7: - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} + '@babel/helper-split-export-declaration@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-string-parser@7.24.7: - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} - engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.7': {} - /@babel/helper-validator-identifier@7.24.7: - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': {} - /@babel/helper-validator-option@7.24.7: - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.24.7': {} - /@babel/helpers@7.24.7: - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} - engines: {node: '>=6.9.0'} + '@babel/helpers@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - /@babel/highlight@7.24.7: - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - /@babel/parser@7.24.7: - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} - engines: {node: '>=6.0.0'} - hasBin: true + '@babel/parser@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/template@7.24.7: - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} - engines: {node: '>=6.9.0'} + '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - /@babel/traverse@7.24.7: - resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} - engines: {node: '>=6.9.0'} + '@babel/traverse@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/generator': 7.24.7 @@ -5974,488 +10135,181 @@ packages: transitivePeerDependencies: - supports-color - /@babel/types@7.24.7: - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} - engines: {node: '>=6.9.0'} + '@babel/types@7.24.7': dependencies: '@babel/helper-string-parser': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - /@balena/dockerignore@1.0.2: - resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - dev: true + '@balena/dockerignore@1.0.2': {} - /@colors/colors@1.6.0: - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - engines: {node: '>=0.1.90'} - dev: false + '@colors/colors@1.6.0': {} - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true - /@dabh/diagnostics@2.0.3: - resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@dabh/diagnostics@2.0.3': 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} + '@es-joy/jsdoccomment@0.36.1': dependencies: comment-parser: 1.3.1 esquery: 1.5.0 jsdoc-type-pratt-parser: 3.1.0 - dev: true - - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/aix-ppc64@0.23.1: - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.21.5': optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.23.1': optional: true - /@esbuild/android-arm64@0.23.1: - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.21.5': optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.23.1': optional: true - /@esbuild/android-arm@0.23.1: - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.21.5': optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.23.1': optional: true - /@esbuild/android-x64@0.23.1: - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-x64@0.21.5': optional: true - - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + + '@esbuild/android-x64@0.23.1': optional: true - /@esbuild/darwin-arm64@0.23.1: - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.21.5': optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.23.1': optional: true - /@esbuild/darwin-x64@0.23.1: - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.21.5': optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.23.1': optional: true - /@esbuild/freebsd-arm64@0.23.1: - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.21.5': optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.23.1': optional: true - /@esbuild/freebsd-x64@0.23.1: - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.21.5': optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.23.1': optional: true - /@esbuild/linux-arm64@0.23.1: - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.21.5': optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.23.1': optional: true - /@esbuild/linux-arm@0.23.1: - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.21.5': optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.23.1': optional: true - /@esbuild/linux-ia32@0.23.1: - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.21.5': optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.23.1': optional: true - /@esbuild/linux-loong64@0.23.1: - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.21.5': optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.23.1': optional: true - /@esbuild/linux-mips64el@0.23.1: - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.21.5': optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.23.1': optional: true - /@esbuild/linux-ppc64@0.23.1: - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.21.5': optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.23.1': optional: true - /@esbuild/linux-riscv64@0.23.1: - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.21.5': optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.23.1': optional: true - /@esbuild/linux-s390x@0.23.1: - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.21.5': optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.23.1': optional: true - /@esbuild/linux-x64@0.23.1: - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.21.5': optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.23.1': optional: true - /@esbuild/netbsd-x64@0.23.1: - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.21.5': optional: true - /@esbuild/openbsd-arm64@0.23.1: - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.23.1': optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-arm64@0.23.1': optional: true - /@esbuild/openbsd-x64@0.23.1: - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.21.5': optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.23.1': optional: true - /@esbuild/sunos-x64@0.23.1: - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.21.5': optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.23.1': optional: true - /@esbuild/win32-arm64@0.23.1: - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.21.5': optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.23.1': optional: true - /@esbuild/win32-ia32@0.23.1: - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.21.5': optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.23.1': optional: true - /@esbuild/win32-x64@0.23.1: - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-x64@0.21.5': optional: 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} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@esbuild/win32-x64@0.23.1': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/regexpp@4.11.0: - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true + '@eslint-community/regexpp@4.11.0': {} - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 debug: 4.3.5 @@ -6468,147 +10322,98 @@ packages: strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - 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} - dev: true + '@eslint/js@8.57.0': {} - /@ewoudenberg/difflib@0.1.0: - resolution: {integrity: sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==} + '@ewoudenberg/difflib@0.1.0': dependencies: heap: 0.2.7 - dev: false - /@faker-js/faker@8.4.1: - resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} - dev: true + '@faker-js/faker@8.4.1': {} - /@humanwhocodes/config-array@0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.5 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/module-importer@1.0.1': {} - /@humanwhocodes/object-schema@2.0.3: - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - dev: true + '@humanwhocodes/object-schema@2.0.3': {} - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + string-width-cjs: string-width@4.2.3 strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 + strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true + wrap-ansi-cjs: wrap-ansi@7.0.0 - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/gen-mapping@0.3.5: - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.2': {} - /@jridgewell/set-array@1.2.1: - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/set-array@1.2.1': {} - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.4.15': {} - /@jridgewell/trace-mapping@0.3.25: - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /@jsdevtools/ono@7.1.3: - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@jsdevtools/ono@7.1.3': {} - /@liuli-util/fs-extra@0.1.0: - resolution: {integrity: sha512-eaAyDyMGT23QuRGbITVY3SOJff3G9ekAAyGqB9joAnTBmqvFN+9a1FazOdO70G6IUqgpKV451eBHYSRcOJ/FNQ==} + '@liuli-util/fs-extra@0.1.0': dependencies: '@types/fs-extra': 9.0.13 fs-extra: 10.1.0 - /@mongodb-js/saslprep@1.1.7: - resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + '@mongodb-js/saslprep@1.1.7': dependencies: sparse-bitfield: 3.0.3 - dev: false - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@nodelib/fs.scandir@2.1.5': 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.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - dev: true - /@pagopa/eslint-config@3.0.0(typescript@5.4.5): - resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + '@pagopa/eslint-config@3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5)': dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-config-prettier: 8.10.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.4.5) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0) + eslint-plugin-functional: 4.4.1(eslint@8.57.0)(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(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.10.0)(eslint@8.57.0)(prettier@2.8.8) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) eslint-plugin-react: 7.34.3(eslint@8.57.0) eslint-plugin-sonarjs: 0.13.0(eslint@8.57.0) prettier: 2.8.8 @@ -6618,59 +10423,38 @@ packages: - supports-color - tsutils - typescript - dev: true - /@pagopa/interop-outbound-models@1.0.4-b: - resolution: {integrity: sha512-c1kxjIBCU0gqV2s3bLIdor+fIfvGLZgML2Y26dTdY3ppoXOhJBVTWKVBu5mq8nmZvZj0KnmK7tjeD6NUJjlywg==} + '@pagopa/interop-outbound-models@1.0.4-b': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 zod: 3.23.8 - dev: false - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@protobuf-ts/plugin-framework@2.9.4: - resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + '@protobuf-ts/plugin-framework@2.9.4': dependencies: '@protobuf-ts/runtime': 2.9.4 typescript: 3.9.10 - dev: false - /@protobuf-ts/plugin@2.9.4: - resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} - hasBin: true + '@protobuf-ts/plugin@2.9.4': dependencies: '@protobuf-ts/plugin-framework': 2.9.4 '@protobuf-ts/protoc': 2.9.4 '@protobuf-ts/runtime': 2.9.4 '@protobuf-ts/runtime-rpc': 2.9.4 typescript: 3.9.10 - dev: false - /@protobuf-ts/protoc@2.9.4: - resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} - hasBin: true - dev: false + '@protobuf-ts/protoc@2.9.4': {} - /@protobuf-ts/runtime-rpc@2.9.4: - resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + '@protobuf-ts/runtime-rpc@2.9.4': dependencies: '@protobuf-ts/runtime': 2.9.4 - dev: false - /@protobuf-ts/runtime@2.9.4: - resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + '@protobuf-ts/runtime@2.9.4': {} - /@puppeteer/browsers@2.2.3: - resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==} - engines: {node: '>=18'} - hasBin: true + '@puppeteer/browsers@2.2.3': dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -6683,272 +10467,139 @@ packages: transitivePeerDependencies: - supports-color - /@redis/bloom@1.2.0(@redis/client@1.5.17): - resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/bloom@1.2.0(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/client@1.5.17: - resolution: {integrity: sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==} - engines: {node: '>=14'} + '@redis/client@1.5.17': dependencies: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 - dev: false - /@redis/graph@1.1.1(@redis/client@1.5.17): - resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/graph@1.1.1(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/json@1.0.6(@redis/client@1.5.17): - resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/json@1.0.6(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/search@1.1.6(@redis/client@1.5.17): - resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/search@1.1.6(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/time-series@1.0.5(@redis/client@1.5.17): - resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/time-series@1.0.5(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-x64@4.18.1': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true + '@sinclair/typebox@0.27.8': {} - /@sinonjs/commons@3.0.1: - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - dev: true - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@sinonjs/fake-timers@10.3.0': dependencies: '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/fake-timers@11.3.1: - resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} + '@sinonjs/fake-timers@11.3.1': dependencies: '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/samsam@8.0.2: - resolution: {integrity: sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==} + '@sinonjs/samsam@8.0.2': dependencies: '@sinonjs/commons': 3.0.1 lodash.get: 4.4.2 type-detect: 4.1.0 - dev: true - /@sinonjs/text-encoding@0.7.3: - resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} - dev: true + '@sinonjs/text-encoding@0.7.3': {} - /@smithy/abort-controller@2.2.0: - resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} - engines: {node: '>=14.0.0'} + '@smithy/abort-controller@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/abort-controller@3.1.1: - resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} - engines: {node: '>=16.0.0'} + '@smithy/abort-controller@3.1.1': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/chunked-blob-reader-native@2.2.0: - resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} + '@smithy/chunked-blob-reader-native@2.2.0': dependencies: '@smithy/util-base64': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader-native@3.0.0: - resolution: {integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==} + '@smithy/chunked-blob-reader-native@3.0.0': dependencies: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader@2.2.0: - resolution: {integrity: sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==} + '@smithy/chunked-blob-reader@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader@3.0.0: - resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==} + '@smithy/chunked-blob-reader@3.0.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/config-resolver@2.2.0: - resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} - engines: {node: '>=14.0.0'} + '@smithy/config-resolver@2.2.0': dependencies: '@smithy/node-config-provider': 2.3.0 '@smithy/types': 2.12.0 '@smithy/util-config-provider': 2.3.0 '@smithy/util-middleware': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/config-resolver@3.0.4: - resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.4': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 @@ -6956,9 +10607,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/config-resolver@3.0.5: - resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.5': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 @@ -6966,9 +10615,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/core@2.2.4: - resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} - engines: {node: '>=16.0.0'} + '@smithy/core@2.2.4': dependencies: '@smithy/middleware-endpoint': 3.0.4 '@smithy/middleware-retry': 3.0.7 @@ -6979,9 +10626,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/core@2.4.0: - resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} - engines: {node: '>=16.0.0'} + '@smithy/core@2.4.0': dependencies: '@smithy/middleware-endpoint': 3.1.0 '@smithy/middleware-retry': 3.0.15 @@ -6994,20 +10639,15 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/credential-provider-imds@2.3.0: - resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} - engines: {node: '>=14.0.0'} + '@smithy/credential-provider-imds@2.3.0': dependencies: '@smithy/node-config-provider': 2.3.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 '@smithy/url-parser': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/credential-provider-imds@3.1.3: - resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.1.3': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/property-provider': 3.1.3 @@ -7015,9 +10655,7 @@ packages: '@smithy/url-parser': 3.0.3 tslib: 2.6.3 - /@smithy/credential-provider-imds@3.2.0: - resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.2.0': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/property-provider': 3.1.3 @@ -7025,106 +10663,75 @@ packages: '@smithy/url-parser': 3.0.3 tslib: 2.6.3 - /@smithy/eventstream-codec@2.2.0: - resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} + '@smithy/eventstream-codec@2.2.0': dependencies: '@aws-crypto/crc32': 3.0.0 '@smithy/types': 2.12.0 '@smithy/util-hex-encoding': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-codec@3.1.2: - resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==} + '@smithy/eventstream-codec@3.1.2': dependencies: '@aws-crypto/crc32': 5.2.0 '@smithy/types': 3.3.0 '@smithy/util-hex-encoding': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-browser@2.2.0: - resolution: {integrity: sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-browser@2.2.0': dependencies: '@smithy/eventstream-serde-universal': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-browser@3.0.4: - resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-browser@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-config-resolver@2.2.0: - resolution: {integrity: sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-config-resolver@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-config-resolver@3.0.3: - resolution: {integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-config-resolver@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-node@2.2.0: - resolution: {integrity: sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-node@2.2.0': dependencies: '@smithy/eventstream-serde-universal': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-node@3.0.4: - resolution: {integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-node@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-universal@2.2.0: - resolution: {integrity: sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-serde-universal@2.2.0': dependencies: '@smithy/eventstream-codec': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-universal@3.0.4: - resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-universal@3.0.4': dependencies: '@smithy/eventstream-codec': 3.1.2 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/fetch-http-handler@2.5.0: - resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} + '@smithy/fetch-http-handler@2.5.0': dependencies: '@smithy/protocol-http': 3.3.0 '@smithy/querystring-builder': 2.2.0 '@smithy/types': 2.12.0 '@smithy/util-base64': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/fetch-http-handler@3.2.0: - resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==} + '@smithy/fetch-http-handler@3.2.0': dependencies: '@smithy/protocol-http': 4.0.3 '@smithy/querystring-builder': 3.0.3 @@ -7132,8 +10739,7 @@ packages: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - /@smithy/fetch-http-handler@3.2.4: - resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + '@smithy/fetch-http-handler@3.2.4': dependencies: '@smithy/protocol-http': 4.1.0 '@smithy/querystring-builder': 3.0.3 @@ -7141,130 +10747,95 @@ packages: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - /@smithy/hash-blob-browser@2.2.0: - resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} + '@smithy/hash-blob-browser@2.2.0': dependencies: '@smithy/chunked-blob-reader': 2.2.0 '@smithy/chunked-blob-reader-native': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/hash-blob-browser@3.1.2: - resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==} + '@smithy/hash-blob-browser@3.1.2': dependencies: '@smithy/chunked-blob-reader': 3.0.0 '@smithy/chunked-blob-reader-native': 3.0.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - dev: false - /@smithy/hash-node@2.2.0: - resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} - engines: {node: '>=14.0.0'} + '@smithy/hash-node@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-buffer-from': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/hash-node@3.0.3: - resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} - engines: {node: '>=16.0.0'} + '@smithy/hash-node@3.0.3': dependencies: '@smithy/types': 3.3.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/hash-stream-node@2.2.0: - resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} - engines: {node: '>=14.0.0'} + '@smithy/hash-stream-node@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/hash-stream-node@3.1.2: - resolution: {integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==} - engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@3.1.2': dependencies: '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/invalid-dependency@2.2.0: - resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} + '@smithy/invalid-dependency@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/invalid-dependency@3.0.3: - resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/is-array-buffer@2.2.0: - resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} - engines: {node: '>=14.0.0'} + '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.6.3 - /@smithy/is-array-buffer@3.0.0: - resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} - engines: {node: '>=16.0.0'} + '@smithy/is-array-buffer@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/md5-js@2.2.0: - resolution: {integrity: sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==} + '@smithy/md5-js@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/md5-js@3.0.3: - resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==} + '@smithy/md5-js@3.0.3': dependencies: '@smithy/types': 3.3.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-content-length@2.2.0: - resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-content-length@2.2.0': dependencies: '@smithy/protocol-http': 3.3.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-content-length@3.0.3: - resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.3': dependencies: '@smithy/protocol-http': 4.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/middleware-content-length@3.0.5: - resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.5': dependencies: '@smithy/protocol-http': 4.1.0 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/middleware-endpoint@2.5.1: - resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-endpoint@2.5.1': dependencies: '@smithy/middleware-serde': 2.3.0 '@smithy/node-config-provider': 2.3.0 @@ -7273,11 +10844,8 @@ packages: '@smithy/url-parser': 2.2.0 '@smithy/util-middleware': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-endpoint@3.0.4: - resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.0.4': dependencies: '@smithy/middleware-serde': 3.0.3 '@smithy/node-config-provider': 3.1.4 @@ -7287,9 +10855,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/middleware-endpoint@3.1.0: - resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.1.0': dependencies: '@smithy/middleware-serde': 3.0.3 '@smithy/node-config-provider': 3.1.4 @@ -7299,9 +10865,7 @@ packages: '@smithy/util-middleware': 3.0.3 tslib: 2.6.3 - /@smithy/middleware-retry@2.3.1: - resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-retry@2.3.1': dependencies: '@smithy/node-config-provider': 2.3.0 '@smithy/protocol-http': 3.3.0 @@ -7312,11 +10876,8 @@ packages: '@smithy/util-retry': 2.2.0 tslib: 2.6.3 uuid: 9.0.1 - dev: false - /@smithy/middleware-retry@3.0.15: - resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.15': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/protocol-http': 4.1.0 @@ -7328,9 +10889,7 @@ packages: tslib: 2.6.3 uuid: 9.0.1 - /@smithy/middleware-retry@3.0.7: - resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.7': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/protocol-http': 4.1.0 @@ -7342,78 +10901,56 @@ packages: tslib: 2.6.3 uuid: 9.0.1 - /@smithy/middleware-serde@2.3.0: - resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-serde@2.3.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-serde@3.0.3: - resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/middleware-stack@2.2.0: - resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} - engines: {node: '>=14.0.0'} + '@smithy/middleware-stack@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-stack@3.0.3: - resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-config-provider@2.3.0: - resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} - engines: {node: '>=14.0.0'} + '@smithy/node-config-provider@2.3.0': dependencies: '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/node-config-provider@3.1.3: - resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.3': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-config-provider@3.1.4: - resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.4': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-http-handler@2.5.0: - resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} - engines: {node: '>=14.0.0'} + '@smithy/node-http-handler@2.5.0': dependencies: '@smithy/abort-controller': 2.2.0 '@smithy/protocol-http': 3.3.0 '@smithy/querystring-builder': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/node-http-handler@3.1.1: - resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.1.1': dependencies: '@smithy/abort-controller': 3.1.1 '@smithy/protocol-http': 4.1.0 @@ -7421,9 +10958,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/node-http-handler@3.1.4: - resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.1.4': dependencies: '@smithy/abort-controller': 3.1.1 '@smithy/protocol-http': 4.1.0 @@ -7431,121 +10966,82 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/property-provider@2.2.0: - resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} - engines: {node: '>=14.0.0'} + '@smithy/property-provider@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/property-provider@3.1.3: - resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} - engines: {node: '>=16.0.0'} + '@smithy/property-provider@3.1.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/protocol-http@2.0.5: - resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} - engines: {node: '>=14.0.0'} + '@smithy/protocol-http@2.0.5': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/protocol-http@3.3.0: - resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} - engines: {node: '>=14.0.0'} + '@smithy/protocol-http@3.3.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/protocol-http@4.0.3: - resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/protocol-http@4.1.0: - resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.1.0': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/querystring-builder@2.2.0: - resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} - engines: {node: '>=14.0.0'} + '@smithy/querystring-builder@2.2.0': dependencies: '@smithy/types': 2.12.0 '@smithy/util-uri-escape': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/querystring-builder@3.0.3: - resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.3': dependencies: '@smithy/types': 3.3.0 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 - /@smithy/querystring-parser@2.2.0: - resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} - engines: {node: '>=14.0.0'} + '@smithy/querystring-parser@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/querystring-parser@3.0.3: - resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/service-error-classification@2.1.5: - resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} - engines: {node: '>=14.0.0'} + '@smithy/service-error-classification@2.1.5': dependencies: '@smithy/types': 2.12.0 - dev: false - /@smithy/service-error-classification@3.0.3: - resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} - engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.3': dependencies: '@smithy/types': 3.3.0 - /@smithy/shared-ini-file-loader@2.4.0: - resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} - engines: {node: '>=14.0.0'} + '@smithy/shared-ini-file-loader@2.4.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/shared-ini-file-loader@3.1.3: - resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/shared-ini-file-loader@3.1.4: - resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.4': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/signature-v4@2.3.0: - resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} - engines: {node: '>=14.0.0'} + '@smithy/signature-v4@2.3.0': dependencies: '@smithy/is-array-buffer': 2.2.0 '@smithy/types': 2.12.0 @@ -7554,11 +11050,8 @@ packages: '@smithy/util-uri-escape': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/signature-v4@3.1.2: - resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@3.1.2': dependencies: '@smithy/is-array-buffer': 3.0.0 '@smithy/types': 3.3.0 @@ -7568,9 +11061,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/signature-v4@4.1.0: - resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@4.1.0': dependencies: '@smithy/is-array-buffer': 3.0.0 '@smithy/protocol-http': 4.1.0 @@ -7581,9 +11072,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/smithy-client@2.5.1: - resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} - engines: {node: '>=14.0.0'} + '@smithy/smithy-client@2.5.1': dependencies: '@smithy/middleware-endpoint': 2.5.1 '@smithy/middleware-stack': 2.2.0 @@ -7591,11 +11080,8 @@ packages: '@smithy/types': 2.12.0 '@smithy/util-stream': 2.2.0 tslib: 2.6.3 - dev: false - /@smithy/smithy-client@3.1.5: - resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.1.5': dependencies: '@smithy/middleware-endpoint': 3.0.4 '@smithy/middleware-stack': 3.0.3 @@ -7604,9 +11090,7 @@ packages: '@smithy/util-stream': 3.0.5 tslib: 2.6.3 - /@smithy/smithy-client@3.2.0: - resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.2.0': dependencies: '@smithy/middleware-endpoint': 3.1.0 '@smithy/middleware-stack': 3.0.3 @@ -7615,116 +11099,81 @@ packages: '@smithy/util-stream': 3.1.3 tslib: 2.6.3 - /@smithy/types@2.12.0: - resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} - engines: {node: '>=14.0.0'} + '@smithy/types@2.12.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/types@3.3.0: - resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==} - engines: {node: '>=16.0.0'} + '@smithy/types@3.3.0': dependencies: tslib: 2.6.3 - /@smithy/url-parser@2.2.0: - resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} + '@smithy/url-parser@2.2.0': dependencies: '@smithy/querystring-parser': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/url-parser@3.0.3: - resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + '@smithy/url-parser@3.0.3': dependencies: '@smithy/querystring-parser': 3.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-base64@2.3.0: - resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} - engines: {node: '>=14.0.0'} + '@smithy/util-base64@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/util-base64@3.0.0: - resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-base64@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-body-length-browser@2.2.0: - resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} + '@smithy/util-body-length-browser@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-body-length-browser@3.0.0: - resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + '@smithy/util-body-length-browser@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-body-length-node@2.3.0: - resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} - engines: {node: '>=14.0.0'} + '@smithy/util-body-length-node@2.3.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-body-length-node@3.0.0: - resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} - engines: {node: '>=16.0.0'} + '@smithy/util-body-length-node@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-buffer-from@2.2.0: - resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} - engines: {node: '>=14.0.0'} + '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 tslib: 2.6.3 - /@smithy/util-buffer-from@3.0.0: - resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} - engines: {node: '>=16.0.0'} + '@smithy/util-buffer-from@3.0.0': dependencies: '@smithy/is-array-buffer': 3.0.0 tslib: 2.6.3 - /@smithy/util-config-provider@2.3.0: - resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} - engines: {node: '>=14.0.0'} + '@smithy/util-config-provider@2.3.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-config-provider@3.0.0: - resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-config-provider@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-defaults-mode-browser@2.2.1: - resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@2.2.1': dependencies: '@smithy/property-provider': 2.2.0 '@smithy/smithy-client': 2.5.1 '@smithy/types': 2.12.0 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@smithy/util-defaults-mode-browser@3.0.15: - resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.15': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/smithy-client': 3.2.0 @@ -7732,9 +11181,7 @@ packages: bowser: 2.11.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-browser@3.0.7: - resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.7': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/smithy-client': 3.2.0 @@ -7742,9 +11189,7 @@ packages: bowser: 2.11.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-node@2.3.1: - resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@2.3.1': dependencies: '@smithy/config-resolver': 2.2.0 '@smithy/credential-provider-imds': 2.3.0 @@ -7753,11 +11198,8 @@ packages: '@smithy/smithy-client': 2.5.1 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-defaults-mode-node@3.0.15: - resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.15': dependencies: '@smithy/config-resolver': 3.0.5 '@smithy/credential-provider-imds': 3.2.0 @@ -7767,9 +11209,7 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-node@3.0.7: - resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.7': dependencies: '@smithy/config-resolver': 3.0.5 '@smithy/credential-provider-imds': 3.1.3 @@ -7779,70 +11219,49 @@ packages: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-endpoints@2.0.4: - resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.0.4': dependencies: '@smithy/node-config-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-endpoints@2.0.5: - resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.0.5': dependencies: '@smithy/node-config-provider': 3.1.4 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-hex-encoding@2.2.0: - resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} - engines: {node: '>=14.0.0'} + '@smithy/util-hex-encoding@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-hex-encoding@3.0.0: - resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-middleware@2.2.0: - resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} - engines: {node: '>=14.0.0'} + '@smithy/util-middleware@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-middleware@3.0.3: - resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} - engines: {node: '>=16.0.0'} + '@smithy/util-middleware@3.0.3': dependencies: '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-retry@2.2.0: - resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} - engines: {node: '>= 14.0.0'} + '@smithy/util-retry@2.2.0': dependencies: '@smithy/service-error-classification': 2.1.5 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-retry@3.0.3: - resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} - engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.3': dependencies: '@smithy/service-error-classification': 3.0.3 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@smithy/util-stream@2.2.0: - resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} - engines: {node: '>=14.0.0'} + '@smithy/util-stream@2.2.0': dependencies: '@smithy/fetch-http-handler': 2.5.0 '@smithy/node-http-handler': 2.5.0 @@ -7852,11 +11271,8 @@ packages: '@smithy/util-hex-encoding': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/util-stream@3.0.5: - resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.0.5': dependencies: '@smithy/fetch-http-handler': 3.2.4 '@smithy/node-http-handler': 3.1.4 @@ -7867,9 +11283,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-stream@3.1.3: - resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.1.3': dependencies: '@smithy/fetch-http-handler': 3.2.4 '@smithy/node-http-handler': 3.1.4 @@ -7880,312 +11294,205 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-uri-escape@2.2.0: - resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} - engines: {node: '>=14.0.0'} + '@smithy/util-uri-escape@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-uri-escape@3.0.0: - resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} - engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-utf8@2.3.0: - resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} - engines: {node: '>=14.0.0'} + '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 tslib: 2.6.3 - /@smithy/util-utf8@3.0.0: - resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} - engines: {node: '>=16.0.0'} + '@smithy/util-utf8@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 tslib: 2.6.3 - /@smithy/util-waiter@2.2.0: - resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} - engines: {node: '>=14.0.0'} + '@smithy/util-waiter@2.2.0': dependencies: '@smithy/abort-controller': 2.2.0 '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-waiter@3.1.2: - resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==} - engines: {node: '>=16.0.0'} + '@smithy/util-waiter@3.1.2': dependencies: '@smithy/abort-controller': 3.1.1 '@smithy/types': 3.3.0 tslib: 2.6.3 - /@testcontainers/postgresql@10.9.0: - resolution: {integrity: sha512-Z3K/TFkl/PVE2v8A6yKqgF4pSFk9ilFG02yeGhPswUjmBlcig/rpVOjBQOkQ/yJCcQ/r2RrX3RR+7vr+UO4QlQ==} + '@testcontainers/postgresql@10.9.0': dependencies: testcontainers: 10.9.0 transitivePeerDependencies: - encoding - supports-color - dev: true - /@tootallnate/quickjs-emscripten@0.23.0: - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@tootallnate/quickjs-emscripten@0.23.0': {} - /@tsconfig/node-lts@20.1.3: - resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} - dev: true + '@tsconfig/node-lts@20.1.3': {} - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true + '@tsconfig/node10@1.0.11': {} - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true + '@tsconfig/node12@1.0.11': {} - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true + '@tsconfig/node14@1.0.3': {} - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true + '@tsconfig/node16@1.0.4': {} - /@tsconfig/strictest@2.0.5: - resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} - dev: true + '@tsconfig/strictest@2.0.5': {} - /@types/adm-zip@0.5.5: - resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} + '@types/adm-zip@0.5.5': dependencies: '@types/node': 20.14.6 - dev: true - /@types/body-parser@1.19.5: - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 '@types/node': 20.14.6 - /@types/buffers@0.1.31: - resolution: {integrity: sha512-wEZBb3o0Kh5RAj3V172vJCcxaCV8C2HJ7YLBBlG5Mwue0g4uRg5LWv8C6ap8MyFbXE6UbYEuvtHY7oTWAPeXEw==} + '@types/buffers@0.1.31': dependencies: '@types/node': 20.14.6 - dev: false - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/connect@3.4.38': dependencies: '@types/node': 20.14.6 - /@types/docker-modem@3.0.6: - resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + '@types/docker-modem@3.0.6': dependencies: '@types/node': 20.14.6 '@types/ssh2': 1.15.0 - dev: true - /@types/dockerode@3.3.29: - resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==} + '@types/dockerode@3.3.29': dependencies: '@types/docker-modem': 3.0.6 '@types/node': 20.14.6 '@types/ssh2': 1.15.0 - dev: true - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/express-serve-static-core@4.19.5: - resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + '@types/express-serve-static-core@4.19.5': dependencies: '@types/node': 20.14.6 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - /@types/express@4.17.21: - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.19.5 '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 - /@types/fs-extra@9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + '@types/fs-extra@9.0.13': dependencies: '@types/node': 20.14.6 - /@types/html2json@1.0.1: - resolution: {integrity: sha512-D+cq6HcgfMdrbIpXzxsmJ9mBz9VlyagqaIB9OZVHGrOeyWbwjTLWR2qUGh1w/VZLtG4wy8mp7v4EpJQ1dYqWzw==} - dev: true + '@types/html2json@1.0.1': {} - /@types/http-errors@2.0.4: - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + '@types/http-errors@2.0.4': {} - /@types/json-diff@1.0.3: - resolution: {integrity: sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==} - dev: true + '@types/json-diff@1.0.3': {} - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true + '@types/json-schema@7.0.15': {} - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true + '@types/json5@0.0.29': {} - /@types/jsonwebtoken@9.0.6: - resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + '@types/jsonwebtoken@9.0.6': dependencies: '@types/node': 20.14.6 - /@types/lodash.isequal@4.5.8: - resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - dev: true - /@types/lodash@4.14.196: - resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} - dev: true + '@types/lodash@4.14.196': {} - /@types/lodash@4.17.6: - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} - dev: true + '@types/lodash@4.17.6': {} - /@types/mime@1.3.5: - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/mime@1.3.5': {} - /@types/multer@1.4.11: - resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + '@types/multer@1.4.11': dependencies: '@types/express': 4.17.21 - dev: true - /@types/node@18.19.39: - resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + '@types/node@18.19.39': dependencies: undici-types: 5.26.5 - dev: true - /@types/node@20.14.6: - resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + '@types/node@20.14.6': dependencies: undici-types: 5.26.5 - /@types/node@20.4.9: - resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==} - dev: true + '@types/node@20.4.9': {} - /@types/nodemailer@6.4.15: - resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} + '@types/nodemailer@6.4.15': dependencies: '@types/node': 20.14.6 - dev: true - /@types/nodemailer@6.4.9: - resolution: {integrity: sha512-XYG8Gv+sHjaOtUpiuytahMy2mM3rectgroNbs6R3djZEKmPNiIJwe9KqOJBGzKKnNZNKvnuvmugBgpq3w/S0ig==} + '@types/nodemailer@6.4.9': dependencies: '@types/node': 20.14.6 - dev: true - /@types/qs@6.9.15: - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + '@types/qs@6.9.15': {} - /@types/range-parser@1.2.7: - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/range-parser@1.2.7': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/send@0.17.4: - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 '@types/node': 20.14.6 - /@types/serve-static@1.15.7: - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 '@types/node': 20.14.6 '@types/send': 0.17.4 - /@types/sinon@10.0.20: - resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} + '@types/sinon@10.0.20': dependencies: '@types/sinonjs__fake-timers': 8.1.5 - dev: true - /@types/sinonjs__fake-timers@8.1.5: - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - dev: true + '@types/sinonjs__fake-timers@8.1.5': {} - /@types/ssh2-sftp-client@9.0.4: - resolution: {integrity: sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==} + '@types/ssh2-sftp-client@9.0.4': dependencies: '@types/ssh2': 1.15.0 - dev: true - /@types/ssh2-streams@0.1.12: - resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + '@types/ssh2-streams@0.1.12': dependencies: '@types/node': 20.14.6 - dev: true - /@types/ssh2@0.5.52: - resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + '@types/ssh2@0.5.52': dependencies: '@types/node': 20.14.6 '@types/ssh2-streams': 0.1.12 - dev: true - /@types/ssh2@1.15.0: - resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==} + '@types/ssh2@1.15.0': dependencies: '@types/node': 18.19.39 - dev: true - /@types/triple-beam@1.3.5: - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - dev: false + '@types/triple-beam@1.3.5': {} - /@types/webidl-conversions@7.0.3: - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false + '@types/webidl-conversions@7.0.3': {} - /@types/whatwg-url@11.0.5: - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 - dev: false - /@types/yauzl@2.10.3: - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - requiresBuild: true + '@types/yauzl@2.10.3': dependencies: '@types/node': 20.14.6 optional: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - 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 + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) @@ -8199,72 +11506,43 @@ packages: natural-compare-lite: 1.4.0 semver: 7.6.2 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - 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 + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) debug: 4.3.5 eslint: 8.57.0 + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/scope-manager@5.62.0: - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) debug: 4.3.5 eslint: 8.57.0 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/types@5.62.0: - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@typescript-eslint/types@5.62.0': {} - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5): - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 @@ -8273,16 +11551,12 @@ packages: is-glob: 4.0.3 semver: 7.6.2 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 @@ -8296,199 +11570,120 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/visitor-keys@5.62.0: - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/visitor-keys@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - dev: true - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - dev: true + '@ungap/structured-clone@1.2.0': {} - /@vitest/expect@1.6.0: - resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + '@vitest/expect@1.6.0': dependencies: '@vitest/spy': 1.6.0 '@vitest/utils': 1.6.0 chai: 4.4.1 - dev: true - /@vitest/runner@1.6.0: - resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + '@vitest/runner@1.6.0': dependencies: '@vitest/utils': 1.6.0 p-limit: 5.0.0 pathe: 1.1.2 - dev: true - /@vitest/snapshot@1.6.0: - resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + '@vitest/snapshot@1.6.0': dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 - dev: true - /@vitest/spy@1.6.0: - resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + '@vitest/spy@1.6.0': dependencies: tinyspy: 2.2.1 - dev: true - /@vitest/utils@1.6.0: - resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + '@vitest/utils@1.6.0': dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 loupe: 2.3.7 pretty-format: 29.7.0 - dev: true - /@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8): - resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} - peerDependencies: - axios: ^0.x || ^1.0.0 - zod: ^3.x + '@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8)': dependencies: axios: 1.7.4 zod: 3.23.8 - /@zodios/express@10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8): - resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} - peerDependencies: - '@zodios/core': '>=10.4.4 <11.0.0' - express: 4.x - zod: ^3.x + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8)': dependencies: '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) express: 4.20.0 zod: 3.23.8 - dev: false - /accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: false - /acorn-jsx@2.0.1: - resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + acorn-jsx@2.0.1: dependencies: acorn: 2.7.0 - dev: true - /acorn-jsx@5.3.2(acorn@8.12.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: true - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 - dev: true - /acorn@2.7.0: - resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true + acorn@2.7.0: {} - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true + acorn@8.12.1: {} - /adm-zip@0.5.15: - resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} - engines: {node: '>=12.0'} - dev: false + adm-zip@0.5.15: {} - /agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} - engines: {node: '>= 14'} + agent-base@7.1.1: dependencies: debug: 4.3.5 transitivePeerDependencies: - supports-color - /ajv-draft-04@1.0.0(ajv@8.16.0): - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: + ajv-draft-04@1.0.0(ajv@8.16.0): + optionalDependencies: ajv: 8.16.0 - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.12.6: 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 - /ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true + ansi-styles@5.2.0: {} - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /append-field@1.0.0: - resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - dev: false + append-field@1.0.0: {} - /archiver-utils@2.1.0: - resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} - engines: {node: '>= 6'} + archiver-utils@2.1.0: dependencies: glob: 7.2.3 graceful-fs: 4.2.11 @@ -8500,11 +11695,8 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 2.3.8 - dev: true - /archiver-utils@3.0.4: - resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} - engines: {node: '>= 10'} + archiver-utils@3.0.4: dependencies: glob: 7.2.3 graceful-fs: 4.2.11 @@ -8516,11 +11708,8 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 3.6.2 - dev: true - /archiver@5.3.2: - resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} - engines: {node: '>= 10'} + archiver@5.3.2: dependencies: archiver-utils: 2.1.0 async: 3.2.5 @@ -8529,35 +11718,23 @@ packages: readdir-glob: 1.1.3 tar-stream: 2.2.0 zip-stream: 4.1.1 - dev: true - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true + arg@4.1.3: {} - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 - dev: true - /array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - dev: false + array-flatten@1.1.1: {} - /array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -8565,16 +11742,10 @@ packages: es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 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-union@2.1.0: {} - /array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -8582,11 +11753,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -8594,51 +11762,37 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + array.prototype.toreversed@1.1.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - dev: true - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -8648,76 +11802,53 @@ packages: get-intrinsic: 1.2.4 is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 - dev: true - /asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + asn1@0.2.6: dependencies: safer-buffer: 2.1.2 - /assert-options@0.8.1: - resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} - engines: {node: '>=10.0.0'} + assert-options@0.8.1: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} + ast-types@0.13.4: dependencies: tslib: 2.6.3 - /async-lock@1.4.1: - resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} - dev: true + async-lock@1.4.1: {} - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.5: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + asynckit@0.4.0: {} - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - dev: true - /aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.645.0): - resolution: {integrity: sha512-L0Jk0k2XNHMSGipJ8rRdTq51KrH/gwrfZ39iKY9BWHGOAv7EygsG4qJC7lIRsbu5/ZHB886Z3WsOsFxqR2R4XQ==} - engines: {node: '>=14.x'} + aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)): dependencies: '@aws-crypto/sha256-js': 4.0.0 '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.645.0) + '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/util-format-url': 3.609.0 '@smithy/signature-v4': 2.3.0 '@types/buffers': 0.1.31 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /aws-sdk-client-mock@4.0.1: - resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==} + aws-sdk-client-mock@4.0.1: dependencies: '@types/sinon': 10.0.20 sinon: 16.1.3 tslib: 2.6.3 - dev: true - /axios-logger@2.8.1: - resolution: {integrity: sha512-Bbl7XRR/Rkxg2Owv/kRgAZ/0qf8kMPLc08LtiUcGCWV5RmoI7vHr+eee6SUc8jRi2nE5KWShziCVh35C1SBEaw==} + axios-logger@2.8.1: dependencies: chalk: 4.1.2 dateformat: 3.0.3 - dev: false - /axios@1.7.4: - resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + axios@1.7.4: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -8725,69 +11856,48 @@ packages: transitivePeerDependencies: - debug - /b4a@1.6.6: - resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + b4a@1.6.6: {} - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /bare-events@2.4.2: - resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} - requiresBuild: true + bare-events@2.4.2: optional: true - /bare-fs@2.3.1: - resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} - requiresBuild: true + bare-fs@2.3.1: dependencies: bare-events: 2.4.2 bare-path: 2.1.3 bare-stream: 2.1.3 optional: true - /bare-os@2.4.0: - resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} - requiresBuild: true + bare-os@2.4.0: optional: true - /bare-path@2.1.3: - resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} - requiresBuild: true + bare-path@2.1.3: dependencies: bare-os: 2.4.0 optional: true - /bare-stream@2.1.3: - resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} - requiresBuild: true + bare-stream@2.1.3: dependencies: streamx: 2.18.0 optional: true - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64-js@1.5.1: {} - /basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} + basic-ftp@5.0.5: {} - /bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@1.20.3: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -8803,92 +11913,56 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /bowser@2.11.0: - resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + bowser@2.11.0: {} - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /browserslist@4.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.1: dependencies: caniuse-lite: 1.0.30001640 electron-to-chromium: 1.4.818 node-releases: 2.0.14 update-browserslist-db: 1.1.0(browserslist@4.23.1) - /bson@6.8.0: - resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} - engines: {node: '>=16.20.1'} - dev: false + bson@6.8.0: {} - /buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-crc32@0.2.13: {} - /buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-equal-constant-time@1.0.1: {} - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false + buffer-from@1.1.2: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /buildcheck@0.0.6: - resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} - engines: {node: '>=10.0.0'} - requiresBuild: true + buildcheck@0.0.6: optional: true - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 - dev: false - /byline@5.0.0: - resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} - engines: {node: '>=0.10.0'} - dev: true + byline@5.0.0: {} - /bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - dev: false + bytes@3.1.2: {} - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + cac@6.7.14: {} - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 @@ -8896,19 +11970,13 @@ packages: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - /call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + call-me-maybe@1.0.2: {} - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /caniuse-lite@1.0.30001640: - resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} + caniuse-lite@1.0.30001640: {} - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} + chai@4.4.1: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -8917,212 +11985,133 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 - dev: true - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - dev: true + chownr@1.1.4: {} - /chromium-bidi@0.5.23(devtools-protocol@0.0.1299070): - resolution: {integrity: sha512-1o/gLU9wDqbN5nL2MtfjykjOuighGXc3/hnWueO1haiEoFgX8h5vbvcA4tgdQfjw1mkZ1OEF4x/+HVeqEX6NoA==} - peerDependencies: - devtools-protocol: '*' + chromium-bidi@0.5.23(devtools-protocol@0.0.1299070): dependencies: devtools-protocol: 0.0.1299070 mitt: 3.0.1 urlpattern-polyfill: 10.0.0 zod: 3.23.8 - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - /cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - dev: false + cluster-key-slot@1.1.2: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@3.2.1: dependencies: color-convert: 1.9.3 color-string: 1.9.1 - dev: false - /colors@1.4.0: - resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} - engines: {node: '>=0.1.90'} - dev: false + colors@1.4.0: {} - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + colorspace@1.1.4: dependencies: color: 3.2.1 text-hex: 1.0.0 - dev: false - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - /comment-parser@1.3.1: - resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} - engines: {node: '>= 12.0.0'} - dev: true + comment-parser@1.3.1: {} - /compress-commons@4.1.2: - resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} - engines: {node: '>= 10'} + compress-commons@4.1.2: dependencies: buffer-crc32: 0.2.13 crc32-stream: 4.0.3 normalize-path: 3.0.0 readable-stream: 3.6.2 - dev: true - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + concat-map@0.0.1: {} - /concat-stream@1.6.2: - resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} - engines: {'0': node >= 0.8} + concat-stream@1.6.2: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 readable-stream: 2.3.8 typedarray: 0.0.6 - dev: false - /concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} + concat-stream@2.0.0: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 readable-stream: 3.6.2 typedarray: 0.0.6 - dev: false - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true + confbox@0.1.7: {} - /connection-string@4.4.0: - resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} - engines: {node: '>=14'} - dev: false + connection-string@4.4.0: {} - /console-assert@1.0.0: - resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} - dev: true + console-assert@1.0.0: {} - /content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 - dev: false - /content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - dev: false + content-type@1.0.5: {} - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convert-source-map@2.0.0: {} - /cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - dev: false + cookie-signature@1.0.6: {} - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.6.0: {} - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + core-util-is@1.0.3: {} - /cosmiconfig@9.0.0(typescript@5.4.5): - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@9.0.0(typescript@5.4.5): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 + optionalDependencies: typescript: 5.4.5 - /cpu-features@0.0.10: - resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} - engines: {node: '>=10.0.0'} - requiresBuild: true + cpu-features@0.0.10: dependencies: buildcheck: 0.0.6 nan: 2.20.0 optional: true - /cpx2@7.0.1: - resolution: {integrity: sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==} - engines: {node: '>=18', npm: '>=10'} - hasBin: true + cpx2@7.0.1: dependencies: debounce: 2.1.0 debug: 4.3.5 @@ -9139,253 +12128,136 @@ packages: subarg: 1.0.0 transitivePeerDependencies: - supports-color - dev: true - /crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - dev: true + crc-32@1.2.2: {} - /crc32-stream@4.0.3: - resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} - engines: {node: '>= 10'} + crc32-stream@4.0.3: dependencies: crc-32: 1.2.2 readable-stream: 3.6.2 - dev: true - /create-eslint-index@1.0.0: - resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} - engines: {node: '>=4.0.0'} + create-eslint-index@1.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 + create-require@1.1.1: {} - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true - /csv-generate@4.4.1: - resolution: {integrity: sha512-O/einO0v4zPmXaOV+sYqGa02VkST4GP5GLpWBNHEouIU7pF3kpGf3D0kCCvX82ydIY4EKkOK+R8b1BYsRXravg==} - dev: false + csv-generate@4.4.1: {} - /csv-parse@5.5.6: - resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==} - dev: false + csv-parse@5.5.6: {} - /csv-stringify@6.5.1: - resolution: {integrity: sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==} - dev: false + csv-stringify@6.5.1: {} - /csv@6.3.2: - resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} - engines: {node: '>= 0.1.90'} + csv@6.3.2: dependencies: csv-generate: 4.4.1 csv-parse: 5.5.6 csv-stringify: 6.5.1 stream-transform: 3.3.2 - dev: false - /data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} + data-uri-to-buffer@6.0.2: {} - /data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /date-fns-tz@3.1.3(date-fns@3.6.0): - resolution: {integrity: sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==} - peerDependencies: - date-fns: ^3.0.0 + date-fns-tz@3.1.3(date-fns@3.6.0): dependencies: date-fns: 3.6.0 - dev: false - /date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + date-fns@3.6.0: {} - /dateformat@3.0.3: - resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} - dev: false + dateformat@3.0.3: {} - /debounce@2.1.0: - resolution: {integrity: sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==} - engines: {node: '>=18'} - dev: true + debounce@2.1.0: {} - /debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@2.6.9: dependencies: ms: 2.0.0 - dev: false - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: true - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.4: dependencies: ms: 2.1.2 - /debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5: dependencies: ms: 2.1.2 - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.0.8 - dev: true - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true + deep-is@0.1.4: {} - /deepmerge-ts@4.3.0: - resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} - engines: {node: '>=12.4.0'} - dev: true + deepmerge-ts@4.3.0: {} - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 gopd: 1.0.1 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - dev: true - /degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 escodegen: 2.1.0 esprima: 4.0.1 - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + delayed-stream@1.0.0: {} - /depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dev: false + depd@2.0.0: {} - /depseek@0.4.1: - resolution: {integrity: sha512-YYfPPajzH9s2qnEva411VJzCMWtArBTfluI9USiKQ+T6xBWFh3C7yPxhaa1KVgJa17v9aRKc+LcRhgxS5/9mOA==} - dev: true + depseek@0.4.1: {} - /destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: false + destroy@1.2.0: {} - /devtools-protocol@0.0.1299070: - resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} + devtools-protocol@0.0.1299070: {} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + diff-sequences@29.6.3: {} - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true + diff@4.0.2: {} - /diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - dev: true + diff@5.2.0: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /docker-compose@0.24.8: - resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} - engines: {node: '>= 6.0.0'} + docker-compose@0.24.8: dependencies: yaml: 2.4.5 - dev: true - /docker-modem@3.0.8: - resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} - engines: {node: '>= 8.0'} + docker-modem@3.0.8: dependencies: debug: 4.3.5 readable-stream: 3.6.2 @@ -9393,117 +12265,70 @@ packages: ssh2: 1.15.0 transitivePeerDependencies: - supports-color - dev: true - /dockerode@3.3.5: - resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} - engines: {node: '>= 8.0'} + dockerode@3.3.5: dependencies: '@balena/dockerignore': 1.0.2 docker-modem: 3.0.8 tar-fs: 2.0.1 transitivePeerDependencies: - supports-color - dev: true - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: true - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + doctrine@3.0.0: dependencies: esutils: 2.0.3 - dev: true - /dotenv-flow@4.1.0: - resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} - engines: {node: '>= 12.0.0'} + dotenv-flow@4.1.0: dependencies: dotenv: 16.4.5 - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} + dotenv@16.4.5: {} - /drange@1.1.1: - resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} - engines: {node: '>=4'} - dev: true + drange@1.1.1: {} - /dreamopt@0.8.0: - resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} - engines: {node: '>=0.4.0'} + dreamopt@0.8.0: dependencies: wordwrap: 1.0.0 - dev: false - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: true + duplexer@0.1.2: {} - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 - /ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: false + ee-first@1.1.1: {} - /electron-to-chromium@1.4.818: - resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} + electron-to-chromium@1.4.818: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true + emoji-regex@9.2.2: {} - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false + enabled@2.0.0: {} - /encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@1.0.2: {} - /encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@2.0.0: {} - /end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + end-of-stream@1.4.4: dependencies: once: 1.4.0 - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} + env-paths@2.2.1: {} - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: false + err-code@2.0.3: {} - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - /es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 @@ -9551,21 +12376,14 @@ packages: typed-array-length: 1.0.6 unbox-primitive: 1.0.2 which-typed-array: 1.1.15 - dev: true - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + es-errors@1.3.0: {} - /es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} - engines: {node: '>= 0.4'} + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -9581,44 +12399,28 @@ packages: internal-slot: 1.0.7 iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 - dev: true - /es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 - dev: true - /es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 hasown: 2.0.2 - dev: true - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.0.2: dependencies: hasown: 2.0.2 - dev: true - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - dev: true - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 '@esbuild/android-arm': 0.21.5 @@ -9643,13 +12445,8 @@ packages: '@esbuild/win32-arm64': 0.21.5 '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - dev: true - /esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + esbuild@0.23.1: optionalDependencies: '@esbuild/aix-ppc64': 0.23.1 '@esbuild/android-arm': 0.23.1 @@ -9675,29 +12472,16 @@ packages: '@esbuild/win32-arm64': 0.23.1 '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 - dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} + escalade@3.1.2: {} - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false + escape-html@1.0.3: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true + escape-string-regexp@4.0.0: {} - /escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true + escodegen@2.1.0: dependencies: esprima: 4.0.1 estraverse: 5.3.0 @@ -9705,118 +12489,62 @@ packages: optionalDependencies: source-map: 0.6.1 - /eslint-ast-utils@1.1.0: - resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} - engines: {node: '>=4'} + eslint-ast-utils@1.1.0: dependencies: lodash.get: 4.4.2 lodash.zip: 4.2.0 - dev: true - /eslint-config-prettier@8.10.0(eslint@8.57.0): - resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + eslint-config-prettier@8.10.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.14.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - 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 + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-extra-rules@0.0.0-development: - resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} - engines: {node: '> 0.10.*'} + eslint-plugin-extra-rules@0.0.0-development: 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.57.0): - resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} - engines: {node: '>=4.0.0'} - peerDependencies: - eslint: '>=3' + eslint-plugin-fp@2.3.0(eslint@8.57.0): dependencies: create-eslint-index: 1.0.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.57.0)(typescript@5.4.5): - 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 + eslint-plugin-functional@4.4.1(eslint@8.57.0)(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) deepmerge-ts: 4.3.0 escape-string-regexp: 4.0.0 eslint: 8.57.0 semver: 7.6.2 + optionalDependencies: + tsutils: 3.21.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -9825,7 +12553,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3 @@ -9835,17 +12563,14 @@ packages: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): - resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} - engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): dependencies: '@es-joy/jsdoccomment': 0.36.1 comment-parser: 1.3.1 @@ -9857,38 +12582,20 @@ packages: spdx-expression-parse: 3.0.1 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): - resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} - peerDependencies: - eslint: '>=2.0.0' + eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.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 + + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: eslint: 8.57.0 - eslint-config-prettier: 8.10.0(eslint@8.57.0) prettier: 2.8.8 prettier-linter-helpers: 1.0.0 - dev: true + optionalDependencies: + eslint-config-prettier: 8.10.0(eslint@8.57.0) - /eslint-plugin-react@7.34.3(eslint@8.57.0): - resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint-plugin-react@7.34.3(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -9909,43 +12616,24 @@ packages: resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.11 - dev: true - /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 + eslint-plugin-sonarjs@0.13.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.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} + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.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-visitor-keys@3.4.3: {} - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true + eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.11.0 @@ -9987,75 +12675,43 @@ packages: 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 + espree@3.0.0-alpha-1: dependencies: acorn: 2.7.0 acorn-jsx: 2.0.1 - 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} + espree@9.6.1: dependencies: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 - dev: true - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true + esprima@4.0.1: {} - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} + esquery@1.5.0: dependencies: estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 - dev: true - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true + estraverse@4.3.0: {} - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} + estraverse@5.3.0: {} - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 - dev: true - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + esutils@2.0.3: {} - /etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - dev: false + etag@1.8.1: {} - /eval-estree-expression@2.0.0: - resolution: {integrity: sha512-e1VweC8biANiuLBY1kZSva3PpaiqaL79S9RR9ql9ngidCYM6tsY20xrUBEJsN63A1yVSmO92chPaBpIGBmGsPg==} + eval-estree-expression@2.0.0: {} - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 get-stream: 8.0.1 @@ -10066,11 +12722,8 @@ packages: onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 - dev: true - /express@4.20.0: - resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} - engines: {node: '>= 0.10.0'} + express@4.20.0: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 @@ -10105,12 +12758,8 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color - dev: false - /extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true + extract-zip@2.0.1: dependencies: debug: 4.3.5 get-stream: 5.2.0 @@ -10120,79 +12769,51 @@ packages: transitivePeerDependencies: - supports-color - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true + fast-diff@1.3.0: {} - /fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-fifo@1.3.2: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: 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.7 - dev: true - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true + fast-levenshtein@2.0.6: {} - /fast-xml-parser@4.2.5: - resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} - hasBin: true + fast-xml-parser@4.2.5: dependencies: strnum: 1.0.5 - /fast-xml-parser@4.4.1: - resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} - hasBin: true + fast-xml-parser@4.4.1: dependencies: strnum: 1.0.5 - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - dev: true - /fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fd-slicer@1.1.0: dependencies: pend: 1.2.0 - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false + fecha@4.2.3: {} - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - dev: true - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} + finalhandler@1.2.0: dependencies: debug: 2.6.9 encodeurl: 1.0.2 @@ -10203,147 +12824,84 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /find-index@0.1.1: - resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} - dev: true + find-index@0.1.1: {} - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@3.2.0: dependencies: flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 - dev: true - /flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - dev: true + flatted@3.3.1: {} - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false + fn.name@1.1.0: {} - /follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true + follow-redirects@1.15.6: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.3: dependencies: is-callable: 1.2.7 - dev: true - /foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - dev: false + forwarded@0.2.0: {} - /fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - dev: false + fresh@0.5.2: {} - /fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true + fs-constants@1.0.0: {} - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - /fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 functions-have-names: 1.2.3 - dev: true - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: true + functions-have-names@1.2.3: {} - /generic-pool@3.9.0: - resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} - engines: {node: '>= 4'} - dev: false + generic-pool@3.9.0: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + get-caller-file@2.0.5: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-func-name@2.0.2: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 @@ -10351,40 +12909,25 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - dev: true + get-port@5.1.1: {} - /get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} + get-stream@5.2.0: dependencies: pump: 3.0.0 - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true + get-stream@8.0.1: {} - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - dev: true - /get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 - dev: true - /get-uri@6.0.3: - resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} - engines: {node: '>= 14'} + get-uri@6.0.3: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 @@ -10393,31 +12936,19 @@ packages: transitivePeerDependencies: - supports-color - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - dev: true - /glob2base@0.0.12: - resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} - engines: {node: '>= 0.10'} + glob2base@0.0.12: dependencies: find-index: 0.1.1 - dev: true - /glob@10.4.3: - resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} - engines: {node: '>=18'} - hasBin: true + glob@10.4.3: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.1 @@ -10425,11 +12956,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -10437,30 +12965,19 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@13.24.0: dependencies: type-fest: 0.20.2 - dev: true - /globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.0.1 - dev: true - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -10468,35 +12985,24 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /globby@13.2.2: - resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + globby@13.2.2: dependencies: dir-glob: 3.0.1 fast-glob: 3.3.2 ignore: 5.3.1 merge2: 1.4.1 slash: 4.0.0 - dev: true - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graceful-fs@4.2.11: {} - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true + graphemer@1.4.0: {} - /handlebars@4.7.7: - resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} - engines: {node: '>=0.4.7'} - hasBin: true + handlebars@4.7.7: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -10504,12 +13010,8 @@ packages: wordwrap: 1.0.0 optionalDependencies: uglify-js: 3.18.0 - dev: true - /handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true + handlebars@4.7.8: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -10518,453 +13020,265 @@ packages: optionalDependencies: uglify-js: 3.18.0 - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true + has-bigints@1.0.2: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.0.3: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - dev: true - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /heap@0.2.7: - resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} - dev: false + heap@0.2.7: {} - /html2json@1.0.2: - resolution: {integrity: sha512-tCdVt82U+/D1GCXFIoN5VfCzx767065EZJ5B8nStQUGSXU9PQ4L/0kFvF2of3Qsoe9HGaJni+lxOkqakfw0zpA==} - dev: false + html2json@1.0.2: {} - /http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} + http-errors@2.0.0: dependencies: depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 statuses: 2.0.1 toidentifier: 1.0.1 - dev: false - /http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 debug: 4.3.5 transitivePeerDependencies: - supports-color - /https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} - engines: {node: '>= 14'} + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 debug: 4.3.5 transitivePeerDependencies: - supports-color - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true + human-signals@5.0.0: {} - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: false - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ieee754@1.2.1: {} - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 - dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - dev: false + ipaddr.js@1.9.1: {} - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.2.1: {} - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + is-arrayish@0.3.2: {} - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - dev: true - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true + is-callable@1.2.7: {} - /is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} + is-core-module@2.14.0: dependencies: hasown: 2.0.2 - dev: true - /is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 - dev: true - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + is-fullwidth-code-point@3.0.0: {} - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} + is-generator-function@1.0.10: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - /is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - dev: true + is-map@2.0.3: {} - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - dev: true + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-number@7.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-path-inside@3.0.3: {} - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - dev: true + is-set@2.0.3: {} - /is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 - dev: true - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false + is-stream@2.0.1: {} - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-stream@3.0.0: {} - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 - dev: true - /is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - dev: true + is-weakmap@2.0.2: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - - /is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} - engines: {node: '>= 0.4'} + + is-weakset@2.0.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@1.0.0: {} - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true + isarray@2.0.5: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 get-intrinsic: 1.2.4 has-symbols: 1.0.3 reflect.getprototypeof: 1.0.6 set-function-name: 2.0.2 - dev: true - /jackspeak@3.4.1: - resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} - engines: {node: '>=18'} + jackspeak@3.4.1: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - dev: false + jose@4.15.9: {} - /jose@5.9.4: - resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} - dev: false + jose@5.9.4: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@4.0.0: {} - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: true + js-tokens@9.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsbn@1.1.0: {} - /jsdoc-type-pratt-parser@3.1.0: - resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} - engines: {node: '>=12.0.0'} - dev: true + jsdoc-type-pratt-parser@3.1.0: {} - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + json-buffer@3.0.1: {} - /json-diff@1.0.6: - resolution: {integrity: sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==} - hasBin: true + json-diff@1.0.6: dependencies: '@ewoudenberg/difflib': 0.1.0 colors: 1.4.0 dreamopt: 0.8.0 - dev: false - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@2.3.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true + json-schema-traverse@0.4.1: {} - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true + json-stable-stringify-without-jsonify@1.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true + json5@2.2.3: {} - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - /jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} - engines: {node: '>=12', npm: '>=6'} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 lodash.includes: 4.3.0 @@ -10977,30 +13291,22 @@ packages: ms: 2.1.3 semver: 7.6.2 - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 object.values: 1.2.0 - dev: true - /just-extend@6.2.0: - resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} - dev: true + just-extend@6.2.0: {} - /jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + jwa@1.4.1: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - /jwks-rsa@3.1.0: - resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} - engines: {node: '>=14'} + jwks-rsa@3.1.0: dependencies: '@types/express': 4.17.21 '@types/jsonwebtoken': 9.0.6 @@ -11010,133 +13316,79 @@ packages: lru-memoizer: 2.3.0 transitivePeerDependencies: - supports-color - dev: false - /jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + jws@3.2.2: dependencies: jwa: 1.4.1 safe-buffer: 5.2.1 - /kafkajs@2.2.4: - resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} - engines: {node: '>=14.0.0'} + kafkajs@2.2.4: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false + kuler@2.0.0: {} - /lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} + lazystream@1.0.1: dependencies: readable-stream: 2.3.8 - dev: true - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - /limiter@1.1.5: - resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} - dev: false + limiter@1.1.5: {} - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lines-and-columns@1.2.4: {} - /local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} + local-pkg@0.5.0: dependencies: mlly: 1.7.1 pkg-types: 1.1.3 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false + lodash.clonedeep@4.5.0: {} - /lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - dev: true + lodash.defaults@4.2.0: {} - /lodash.difference@4.5.0: - resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - dev: true + lodash.difference@4.5.0: {} - /lodash.flatten@4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - dev: true + lodash.flatten@4.4.0: {} - /lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - dev: true + lodash.get@4.4.2: {} - /lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + lodash.includes@4.3.0: {} - /lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + lodash.isboolean@3.0.3: {} - /lodash.isempty@4.4.0: - resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} - dev: false + lodash.isempty@4.4.0: {} - /lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - dev: false + lodash.isequal@4.5.0: {} - /lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + lodash.isinteger@4.0.4: {} - /lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + lodash.isnumber@3.0.3: {} - /lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + lodash.isplainobject@4.0.6: {} - /lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.isstring@4.0.1: {} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true + lodash.merge@4.6.2: {} - /lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash.once@4.1.1: {} - /lodash.union@4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - dev: true + lodash.union@4.6.0: {} - /lodash.zip@4.2.0: - resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} - dev: true + lodash.zip@4.2.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true + lodash@4.17.21: {} - /logform@2.6.0: - resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} - engines: {node: '>= 12.0.0'} + logform@2.6.0: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -11144,245 +13396,129 @@ packages: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 - dev: false - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - dev: true - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lru-cache@10.4.0: - resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} - engines: {node: '>=18'} - dev: true + lru-cache@10.4.0: {} - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} + lru-cache@7.18.3: {} - /lru-memoizer@2.3.0: - resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + lru-memoizer@2.3.0: dependencies: lodash.clonedeep: 4.5.0 lru-cache: 6.0.0 - dev: false - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + make-error@1.3.6: {} - /media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - dev: false + media-typer@0.3.0: {} - /memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false + memory-pager@1.5.0: {} - /meow@12.1.1: - resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} - engines: {node: '>=16.10'} - dev: true + meow@12.1.1: {} - /merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - dev: false + merge-descriptors@1.0.3: {} - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true + merge-stream@2.0.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge2@1.4.1: {} - /methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - dev: false + methods@1.1.2: {} - /micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: false + mime@1.6.0: {} - /mime@4.0.4: - resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} - engines: {node: '>=16'} - hasBin: true - dev: false + mime@4.0.4: {} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true + mimic-fn@4.0.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - dev: true - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimist@1.2.8: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mitt@3.0.1: {} - /mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - dev: true + mkdirp-classic@0.5.3: {} - /mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true + mkdirp@0.5.6: dependencies: minimist: 1.2.8 - dev: false - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@1.0.4: {} - /mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@3.0.1: {} - /mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 ufo: 1.5.3 - dev: true - /mnemonist@0.38.3: - resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + mnemonist@0.38.3: dependencies: obliterator: 1.6.1 - /mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + mongodb-connection-string-url@3.0.1: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - dev: false - /mongodb@6.7.0: - resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true + mongodb@6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3): dependencies: '@mongodb-js/saslprep': 1.1.7 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - dev: false + optionalDependencies: + '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + socks: 2.8.3 - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - dev: false + ms@2.0.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /multer@1.4.5-lts.1: - resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} - engines: {node: '>= 6.0.0'} + multer@1.4.5-lts.1: dependencies: append-field: 1.0.0 busboy: 1.6.0 @@ -11391,189 +13527,111 @@ packages: object-assign: 4.1.1 type-is: 1.6.18 xtend: 4.0.2 - dev: false - /nan@2.20.0: - resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} - requiresBuild: true + nan@2.20.0: optional: true - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true + natural-compare-lite@1.4.0: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true + natural-compare@1.4.0: {} - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false + negotiator@0.6.3: {} - /neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + neo-async@2.6.2: {} - /netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} + netmask@2.0.2: {} - /nise@5.1.9: - resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + nise@5.1.9: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 11.3.1 '@sinonjs/text-encoding': 0.7.3 just-extend: 6.2.0 path-to-regexp: 6.3.0 - dev: true - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - dev: true - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.14: {} - /nodemailer@6.9.14: - resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==} - engines: {node: '>=6.0.0'} - dev: false + nodemailer@6.9.14: {} - /nodemailer@6.9.9: - resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} - engines: {node: '>=6.0.0'} - dev: false + nodemailer@6.9.9: {} - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + object-assign@4.1.1: {} - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} + object-inspect@1.13.2: {} - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true + object-keys@1.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - dev: true - /object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} + object.entries@1.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} + object.groupby@1.0.3: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 - dev: true - /object.hasown@1.1.4: - resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} - engines: {node: '>= 0.4'} + object.hasown@1.1.4: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /obliterator@1.6.1: - resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + obliterator@1.6.1: {} - /on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 - dev: false - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + one-time@1.0.0: dependencies: fn.name: 1.1.0 - dev: false - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - dev: true - /openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + openapi-types@12.1.3: {} - /openapi-zod-client@1.18.1: - resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} - hasBin: true + openapi-zod-client@1.18.1: dependencies: '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) '@liuli-util/fs-extra': 0.1.0 @@ -11595,14 +13653,11 @@ packages: - supports-color - xstate - /openapi3-ts@3.1.0: - resolution: {integrity: sha512-1qKTvCCVoV0rkwUh1zq5o8QyghmwYPuhdvtjv1rFjuOnJToXhQyF8eGjNETQ8QmGjr9Jz/tkAKLITIl2s7dw3A==} + openapi3-ts@3.1.0: dependencies: yaml: 2.4.5 - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: deep-is: 0.1.4 fast-levenshtein: 2.0.6 @@ -11610,37 +13665,22 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 word-wrap: 1.2.5 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} + p-limit@5.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@6.0.0: - resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} - engines: {node: '>=16'} - dev: true + p-map@6.0.0: {} - /pac-proxy-agent@7.0.2: - resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} - engines: {node: '>= 14'} + pac-proxy-agent@7.0.2: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 @@ -11653,48 +13693,27 @@ packages: transitivePeerDependencies: - supports-color - /pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} + pac-resolver@7.0.1: dependencies: degenerator: 5.0.1 netmask: 2.0.2 - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - dev: false + parseurl@1.3.3: {} - /pastable@2.2.1: - resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} - engines: {node: '>=14.x'} - peerDependencies: - react: '>=17' - xstate: '>=4.32.1' - peerDependenciesMeta: - react: - optional: true - xstate: - optional: true + pastable@2.2.1: dependencies: '@babel/core': 7.24.7 ts-toolbelt: 9.6.0 @@ -11702,88 +13721,47 @@ packages: transitivePeerDependencies: - supports-color - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-is-absolute@1.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + path-key@3.1.1: {} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true + path-key@4.0.0: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true + path-parse@1.0.7: {} - /path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.0 minipass: 7.1.2 - dev: true - /path-to-regexp@0.1.10: - resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} - dev: false + path-to-regexp@0.1.10: {} - /path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - dev: true + path-to-regexp@6.3.0: {} - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-type@4.0.0: {} - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true + pathe@1.1.2: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathval@1.1.1: {} - /pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pend@1.2.0: {} - /pg-cloudflare@1.1.1: - resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} - requiresBuild: true + pg-cloudflare@1.1.1: optional: true - /pg-connection-string@2.6.4: - resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + pg-connection-string@2.6.4: {} - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} + pg-int8@1.0.1: {} - /pg-minify@1.6.4: - resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} - engines: {node: '>=14.0.0'} + pg-minify@1.6.4: {} - /pg-pool@3.6.2(pg@8.11.5): - resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} - peerDependencies: - pg: '>=8.0' + pg-pool@3.6.2(pg@8.11.5): dependencies: pg: 8.11.5 - /pg-promise@11.8.0: - resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} - engines: {node: '>=14.0'} + pg-promise@11.8.0: dependencies: assert-options: 0.8.1 pg: 8.11.5 @@ -11792,12 +13770,9 @@ packages: transitivePeerDependencies: - pg-native - /pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + pg-protocol@1.6.1: {} - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 @@ -11805,14 +13780,7 @@ packages: postgres-date: 1.0.7 postgres-interval: 1.2.0 - /pg@8.11.5: - resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true + pg@8.11.5: dependencies: pg-connection-string: 2.6.4 pg-pool: 3.6.2(pg@8.11.5) @@ -11822,134 +13790,83 @@ packages: optionalDependencies: pg-cloudflare: 1.1.1 - /pgpass@1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + pgpass@1.0.5: dependencies: split2: 4.2.0 - /picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.0.1: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picomatch@2.3.1: {} - /pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: true + possible-typed-array-names@1.0.0: {} - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - dev: true - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} + postgres-array@2.0.0: {} - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} + postgres-bytea@1.0.0: {} - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} + postgres-date@1.0.7: {} - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true + prelude-ls@1.2.1: {} - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} + prettier-linter-helpers@1.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 + prettier@2.8.8: {} - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - dev: true - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-nextick-args@2.0.1: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} + progress@2.0.3: {} - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 retry: 0.12.0 - dev: false - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true - /proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 retry: 0.12.0 signal-exit: 3.0.7 - dev: true - /properties-reader@2.3.0: - resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} - engines: {node: '>=14'} + properties-reader@2.3.0: dependencies: mkdirp: 1.0.4 - dev: true - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - dev: false - /proxy-agent@6.4.0: - resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} - engines: {node: '>= 14'} + proxy-agent@6.4.0: dependencies: agent-base: 7.1.1 debug: 4.3.5 @@ -11962,22 +13879,16 @@ packages: transitivePeerDependencies: - supports-color - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@1.1.0: {} - /pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + pump@3.0.0: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /puppeteer-core@22.11.2: - resolution: {integrity: sha512-vQo+YDuePyvj+92Z9cdtxi/HalKf+k/R4tE80nGtQqJRNqU81eHaHkbVfnLszdaLlvwFF5tipnnSCzqWlEddtw==} - engines: {node: '>=18'} + puppeteer-core@22.11.2: dependencies: '@puppeteer/browsers': 2.2.3 chromium-bidi: 0.5.23(devtools-protocol@0.0.1299070) @@ -11989,11 +13900,7 @@ packages: - supports-color - utf-8-validate - /puppeteer@22.11.2(typescript@5.4.5): - resolution: {integrity: sha512-8fjdQSgW0sq7471ftca24J7sXK+jXZ7OW7Gx+NEBFNyXrcTiBfukEI46gNq6hiMhbLEDT30NeylK/1ZoPdlKSA==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + puppeteer@22.11.2(typescript@5.4.5): dependencies: '@puppeteer/browsers': 2.2.3 cosmiconfig: 9.0.0(typescript@5.4.5) @@ -12005,75 +13912,45 @@ packages: - typescript - utf-8-validate - /qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} + qs@6.11.0: dependencies: side-channel: 1.0.6 - dev: false - /qs@6.12.3: - resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} - engines: {node: '>=0.6'} + qs@6.12.3: dependencies: side-channel: 1.0.6 - dev: false - /qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} + qs@6.13.0: dependencies: side-channel: 1.0.6 - dev: false - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /queue-tick@1.0.1: - resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + queue-tick@1.0.1: {} - /quote@0.4.0: - resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} - dev: true + quote@0.4.0: {} - /randexp@0.5.3: - resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} - engines: {node: '>=4'} + randexp@0.5.3: dependencies: drange: 1.1.1 ret: 0.2.2 - dev: true - /range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - dev: false + range-parser@1.2.1: {} - /rate-limiter-flexible@5.0.3: - resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} - dev: false + rate-limiter-flexible@5.0.3: {} - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} + raw-body@2.5.2: dependencies: bytes: 3.1.2 http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: false - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true + react-is@16.13.1: {} - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true + react-is@18.3.1: {} - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -12083,22 +13960,17 @@ packages: string_decoder: 1.1.1 util-deprecate: 1.0.2 - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdir-glob@1.1.3: dependencies: minimatch: 5.1.6 - dev: true - /redis@4.6.15: - resolution: {integrity: sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==} + redis@4.6.15: dependencies: '@redis/bloom': 1.2.0(@redis/client@1.5.17) '@redis/client': 1.5.17 @@ -12106,11 +13978,8 @@ packages: '@redis/json': 1.0.6(@redis/client@1.5.17) '@redis/search': 1.1.6(@redis/client@1.5.17) '@redis/time-series': 1.0.5(@redis/client@1.5.17) - dev: false - /reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} - engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -12119,83 +13988,47 @@ packages: get-intrinsic: 1.2.4 globalthis: 1.0.4 which-builtin-type: 1.1.3 - dev: true - /regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - dev: true - /req-all@0.1.0: - resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} - engines: {node: '>=4'} - dev: true + req-all@0.1.0: {} - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@4.0.0: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true + resolve-pkg-maps@1.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true - /ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - dev: true + ret@0.2.2: {} - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} + retry@0.12.0: {} - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true + reusify@1.0.4: {} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -12216,66 +14049,41 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.18.1 '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 - dev: true - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - dev: true - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 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 + safe-stable-stringify@2.4.3: {} - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + safer-buffer@2.1.2: {} - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true + semver@6.3.1: {} - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.0: dependencies: lru-cache: 6.0.0 - /semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.2: {} - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + send@0.18.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -12292,11 +14100,8 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -12313,11 +14118,8 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /serve-static@1.16.0: - resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} - engines: {node: '>= 0.8.0'} + serve-static@1.16.0: dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 @@ -12325,11 +14127,8 @@ packages: send: 0.18.0 transitivePeerDependencies: - supports-color - dev: false - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -12338,66 +14137,41 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.2 - /set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 - dev: true - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: false + setprototypeof@1.2.0: {} - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - dev: true - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} + side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 object-inspect: 1.13.2 - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + siginfo@2.0.0: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true + signal-exit@4.1.0: {} - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - dev: false - /sinon@16.1.3: - resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} + sinon@16.1.3: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 10.3.0 @@ -12405,25 +14179,14 @@ packages: diff: 5.2.0 nise: 5.1.9 supports-color: 7.2.0 - dev: true - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} - dev: true + slash@4.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smart-buffer@4.2.0: {} - /socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} - engines: {node: '>= 14'} + socks-proxy-agent@8.0.4: dependencies: agent-base: 7.1.1 debug: 4.3.5 @@ -12431,81 +14194,50 @@ packages: transitivePeerDependencies: - supports-color - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true + source-map-js@1.2.0: {} - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} + source-map@0.6.1: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 - dev: true - /spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - dev: true + spdx-license-ids@3.0.18: {} - /spex@3.3.0: - resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} - engines: {node: '>=10.0.0'} + spex@3.3.0: {} - /split-ca@1.0.1: - resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} - dev: true + split-ca@1.0.1: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sprintf-js@1.0.3: {} - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sprintf-js@1.1.3: {} - /ssh-remote-port-forward@1.0.4: - resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + ssh-remote-port-forward@1.0.4: dependencies: '@types/ssh2': 0.5.52 ssh2: 1.15.0 - dev: true - /ssh2-sftp-client@9.1.0: - resolution: {integrity: sha512-Hzdr9OE6GxZjcmyM9tgBSIFVyrHAp9c6U2Y4yBkmYOHoQvZ7pIm27dmltvcmRfxcWiIcg8HBvG5iAikDf+ZuzQ==} - engines: {node: '>=10.24.1'} + ssh2-sftp-client@9.1.0: dependencies: concat-stream: 2.0.0 promise-retry: 2.0.1 ssh2: 1.15.0 - dev: false - /ssh2@1.15.0: - resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} - engines: {node: '>=10.16.0'} - requiresBuild: true + ssh2@1.15.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 @@ -12513,34 +14245,19 @@ packages: cpu-features: 0.0.10 nan: 2.20.0 - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false + stack-trace@0.0.10: {} - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + stackback@0.0.2: {} - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - dev: false + statuses@2.0.1: {} - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true + std-env@3.7.0: {} - /stream-transform@3.3.2: - resolution: {integrity: sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ==} - dev: false + stream-transform@3.3.2: {} - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: false + streamsearch@1.1.0: {} - /streamx@2.18.0: - resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + streamx@2.18.0: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 @@ -12548,26 +14265,19 @@ packages: optionalDependencies: bare-events: 2.4.2 - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} - engines: {node: '>= 0.4'} + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -12581,122 +14291,81 @@ packages: regexp.prototype.flags: 1.5.2 set-function-name: 2.0.2 side-channel: 1.0.6 - dev: true - /string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true + strip-final-newline@3.0.0: {} - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true + strip-json-comments@3.1.1: {} - /strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + strip-literal@2.1.0: dependencies: js-tokens: 9.0.0 - dev: true - /strnum@1.0.5: - resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + strnum@1.0.5: {} - /subarg@1.0.0: - resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + subarg@1.0.0: dependencies: minimist: 1.2.8 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true + supports-preserve-symlinks-flag@1.0.0: {} - /tanu@0.1.13: - resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} + tanu@0.1.13: dependencies: tslib: 2.6.3 typescript: 4.9.5 - /tar-fs@2.0.1: - resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + tar-fs@2.0.1: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - dev: true - /tar-fs@3.0.5: - resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + tar-fs@3.0.5: dependencies: pump: 3.0.0 tar-stream: 3.1.7 @@ -12704,36 +14373,29 @@ packages: bare-fs: 2.3.1 bare-path: 2.1.3 - /tar-fs@3.0.6: - resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + tar-fs@3.0.6: dependencies: pump: 3.0.0 tar-stream: 3.1.7 optionalDependencies: bare-fs: 2.3.1 bare-path: 2.1.3 - dev: true - /tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} + tar-stream@2.2.0: dependencies: bl: 4.1.0 end-of-stream: 1.4.4 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + tar-stream@3.1.7: dependencies: b4a: 1.6.6 fast-fifo: 1.3.2 streamx: 2.18.0 - /testcontainers@10.9.0: - resolution: {integrity: sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==} + testcontainers@10.9.0: dependencies: '@balena/dockerignore': 1.0.2 '@types/dockerode': 3.3.29 @@ -12753,88 +14415,42 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true - /text-decoder@1.1.1: - resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + text-decoder@1.1.1: dependencies: b4a: 1.6.6 - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false + text-hex@1.0.0: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true + text-table@0.2.0: {} - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + through@2.3.8: {} - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - dev: true + tinybench@2.8.0: {} - /tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.0.0'} - dev: true + tinypool@0.8.4: {} - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true + tinyspy@2.2.1: {} - /tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} - engines: {node: '>=14.14'} - dev: true + tmp@0.2.3: {} - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + to-fast-properties@2.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - dev: false + toidentifier@1.0.1: {} - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true + tr46@0.0.3: {} - /tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} + tr46@4.1.1: dependencies: punycode: 2.3.1 - dev: false - /triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - dev: false + triple-beam@1.4.1: {} - /ts-node@10.9.2(@types/node@20.14.6)(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 + ts-node@10.9.2(@types/node@20.14.6)(typescript@5.4.5): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -12851,18 +14467,12 @@ packages: typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - /ts-pattern@5.2.0: - resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + ts-pattern@5.2.0: {} - /ts-toolbelt@9.6.0: - resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + ts-toolbelt@9.6.0: {} - /tsc-esm-fix@2.20.27: - resolution: {integrity: sha512-bfoSY29XN4yRvXgfxc4rtKQPe9Xx02BahWSZ3D4GgBXIWSE+TJ/BXGSrpUIBkrsKIUQv2zA3qiwJVFnUV59Xdw==} - engines: {node: '>=16.0.0'} - hasBin: true + tsc-esm-fix@2.20.27: dependencies: depseek: 0.4.1 fs-extra: 11.2.0 @@ -12870,95 +14480,49 @@ packages: json5: 2.2.3 meow: 12.1.1 tslib: 2.6.3 - dev: true - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: 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==} + tslib@1.14.1: {} - /tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.6.3: {} - /tsutils@3.21.0(typescript@5.4.5): - 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' + tsutils@3.21.0(typescript@5.4.5): dependencies: tslib: 1.14.1 typescript: 5.4.5 - dev: true - /tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} - engines: {node: '>=18.0.0'} - hasBin: true + tsx@4.19.1: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 - dev: true - /turbo-darwin-64@2.0.4: - resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-64@2.0.4: optional: true - /turbo-darwin-arm64@2.0.4: - resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-arm64@2.0.4: optional: true - /turbo-linux-64@2.0.4: - resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-64@2.0.4: optional: true - /turbo-linux-arm64@2.0.4: - resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-arm64@2.0.4: optional: true - /turbo-windows-64@2.0.4: - resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-64@2.0.4: optional: true - /turbo-windows-arm64@2.0.4: - resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-arm64@2.0.4: optional: true - /turbo@2.0.4: - resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} - hasBin: true + turbo@2.0.4: optionalDependencies: turbo-darwin-64: 2.0.4 turbo-darwin-arm64: 2.0.4 @@ -12966,68 +14530,41 @@ packages: turbo-linux-arm64: 2.0.4 turbo-windows-64: 2.0.4 turbo-windows-arm64: 2.0.4 - dev: true - /tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + tweetnacl@0.14.5: {} - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - dev: true + type-detect@4.1.0: {} - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true + type-fest@0.20.2: {} - /type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} + type-fest@3.13.1: {} - /type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} + type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: false - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -13035,11 +14572,8 @@ packages: gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -13047,109 +14581,61 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - dev: true - /typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - dev: false + typedarray@0.0.6: {} - /typescript@3.9.10: - resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: false + typescript@3.9.10: {} - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true + typescript@4.9.5: {} - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true + typescript@5.4.5: {} - /ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - dev: true + ufo@1.5.3: {} - /uglify-js@3.18.0: - resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} - engines: {node: '>=0.8.0'} - hasBin: true - requiresBuild: true + uglify-js@3.18.0: optional: true - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - dev: true - /unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + unbzip2-stream@1.4.3: dependencies: buffer: 5.7.1 through: 2.3.8 - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@5.26.5: {} - /universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} + universalify@2.0.1: {} - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - dev: false + unpipe@1.0.0: {} - /update-browserslist-db@1.1.0(browserslist@4.23.1): - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0(browserslist@4.23.1): dependencies: browserslist: 4.23.1 escalade: 3.1.2 picocolors: 1.0.1 - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + urlpattern-polyfill@10.0.0: {} - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - dev: false + utils-merge@1.0.1: {} - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true + uuid@9.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true + v8-compile-cache-lib@3.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vite-node@1.6.0(@types/node@20.14.6): - resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true + vite-node@1.6.0(@types/node@20.14.6): dependencies: cac: 6.7.14 debug: 4.3.5 @@ -13165,70 +14651,18 @@ packages: - sugarss - supports-color - terser - dev: true - /vite@5.3.3(@types/node@20.14.6): - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - 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 + vite@5.3.3(@types/node@20.14.6): dependencies: - '@types/node': 20.14.6 esbuild: 0.21.5 postcss: 8.4.39 rollup: 4.18.1 optionalDependencies: + '@types/node': 20.14.6 fsevents: 2.3.3 - dev: true - /vitest@1.6.0(@types/node@20.14.6): - 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 + vitest@1.6.0(@types/node@20.14.6): dependencies: - '@types/node': 20.14.6 '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 '@vitest/snapshot': 1.6.0 @@ -13249,6 +14683,8 @@ packages: vite: 5.3.3(@types/node@20.14.6) vite-node: 1.6.0(@types/node@20.14.6) why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.14.6 transitivePeerDependencies: - less - lightningcss @@ -13257,52 +14693,35 @@ packages: - sugarss - supports-color - terser - dev: true - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true + webidl-conversions@3.0.1: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} + whatwg-url@13.0.0: dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true - /whence@2.0.1: - resolution: {integrity: sha512-VtcCE1Pe3BKofF/k+P5xcpuoqQ0f1NJY6TmdUw5kInl9/pEr1ZEFD9+ZOUicf52tvpTbhMS93aWXriu2IQYTTw==} - engines: {node: '>=14'} + whence@2.0.1: dependencies: '@babel/parser': 7.24.7 eval-estree-expression: 2.0.0 - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: 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-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} + which-builtin-type@1.1.3: dependencies: function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 @@ -13316,58 +14735,38 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: true - /which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} + which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.3 - dev: true - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true - /winston-transport@4.7.0: - resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} - engines: {node: '>= 12.0.0'} + winston-transport@4.7.0: dependencies: logform: 2.6.0 readable-stream: 3.6.2 triple-beam: 1.4.1 - dev: false - /winston@3.13.0: - resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} - engines: {node: '>= 12.0.0'} + winston@3.13.0: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 @@ -13380,74 +14779,40 @@ packages: stack-trace: 0.0.10 triple-beam: 1.4.1 winston-transport: 4.7.0 - dev: false - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - dev: true + word-wrap@1.2.5: {} - /wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wordwrap@1.0.0: {} - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrappy@1.0.2: {} - /ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.17.1: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + y18n@5.0.8: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} - engines: {node: '>= 14'} - hasBin: true + yaml@2.4.5: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + yargs-parser@21.1.1: {} - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -13457,44 +14822,25 @@ packages: y18n: 5.0.8 yargs-parser: 21.1.1 - /yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true + yn@3.1.1: {} - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + yocto-queue@0.1.0: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@1.1.1: {} - /zip-stream@4.1.1: - resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} - engines: {node: '>= 10'} + zip-stream@4.1.1: dependencies: archiver-utils: 3.0.4 compress-commons: 4.1.2 readable-stream: 3.6.2 - dev: true - /zod-validation-error@3.3.0(zod@3.23.8): - resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.18.0 + zod-validation-error@3.3.0(zod@3.23.8): dependencies: zod: 3.23.8 - dev: false - /zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.23.8: {} From 070e617244c4f209257fce2e23707214b7b57488 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:44:54 +0100 Subject: [PATCH 009/126] PIN 5565, 5567 - ANAC and IVASS config (#1164) --- .../anac-certified-attributes-importer/src/config/config.ts | 2 +- .../src/config/sftpConfig.ts | 2 +- packages/datalake-data-export/src/index.ts | 5 +++++ .../ivass-certified-attributes-importer/src/config/config.ts | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/anac-certified-attributes-importer/src/config/config.ts b/packages/anac-certified-attributes-importer/src/config/config.ts index bc84118087..0d429e14ec 100644 --- a/packages/anac-certified-attributes-importer/src/config/config.ts +++ b/packages/anac-certified-attributes-importer/src/config/config.ts @@ -16,7 +16,7 @@ const AnacCertifiedAttributesImporterConfig = LoggerConfig.and( z .object({ TENANT_PROCESS_URL: APIEndpoint, - RECORDS_PROCESS_BATCH_SIZE: z.number(), + RECORDS_PROCESS_BATCH_SIZE: z.coerce.number(), ANAC_TENANT_ID: z.string(), }) .transform((c) => ({ diff --git a/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts b/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts index a278f4cf93..9f677fc18a 100644 --- a/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts +++ b/packages/anac-certified-attributes-importer/src/config/sftpConfig.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const SftpConfig = z .object({ SFTP_HOST: z.string(), - SFTP_PORT: z.coerce.number().min(1001), + SFTP_PORT: z.coerce.number(), SFTP_USERNAME: z.string(), SFTP_PASSWORD: z.string(), SFTP_FILENAME_PREFIX: z.string(), diff --git a/packages/datalake-data-export/src/index.ts b/packages/datalake-data-export/src/index.ts index 7207a7180f..eba8c07cd6 100644 --- a/packages/datalake-data-export/src/index.ts +++ b/packages/datalake-data-export/src/index.ts @@ -27,3 +27,8 @@ export const dataLakeService = datalakeServiceBuilder( log.info("Datalake Data Exporter job started"); await dataLakeService.exportData(); log.info("Done!"); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. diff --git a/packages/ivass-certified-attributes-importer/src/config/config.ts b/packages/ivass-certified-attributes-importer/src/config/config.ts index f20bb85f7a..515b13177c 100644 --- a/packages/ivass-certified-attributes-importer/src/config/config.ts +++ b/packages/ivass-certified-attributes-importer/src/config/config.ts @@ -18,7 +18,7 @@ const IvassCertifiedAttributesImporterConfig = LoggerConfig.and( SOURCE_URL: z.string(), HISTORY_BUCKET_NAME: z.string(), TENANT_PROCESS_URL: APIEndpoint, - RECORDS_PROCESS_BATCH_SIZE: z.number(), + RECORDS_PROCESS_BATCH_SIZE: z.coerce.number(), IVASS_TENANT_ID: z.string(), }) .transform((c) => ({ From 6df8c3ee0fedb344954fb2d3c15c9b1f7231d4de Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 6 Nov 2024 10:46:06 +0100 Subject: [PATCH 010/126] Removing leftover TS node usages in favor of TSX (#1165) --- .../package.json | 4 +- packages/dtd-catalog-exporter/package.json | 6 +- packages/one-trust-notices/package.json | 2 +- pnpm-lock.yaml | 112 +----------------- 4 files changed, 12 insertions(+), 112 deletions(-) diff --git a/packages/agreement-platformstate-writer/package.json b/packages/agreement-platformstate-writer/package.json index 4774847616..6aecb86d35 100644 --- a/packages/agreement-platformstate-writer/package.json +++ b/packages/agreement-platformstate-writer/package.json @@ -12,7 +12,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "node --loader ts-node/esm -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, @@ -25,7 +25,7 @@ "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" }, diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 96d58d487c..6d8ea31ecd 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -11,8 +11,8 @@ "format:check": "prettier --check src", "format:write": "prettier --write src", "check": "tsc --project tsconfig.check.json", - "start:migrate": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/index.ts", - "start:verify": "node --loader ts-node/esm -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", + "start:migrate": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "start:verify": "tsx -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", "build": "tsc" }, "keywords": [], @@ -31,7 +31,7 @@ "@types/node": "20.14.6", "eslint": "8.57.0", "prettier": "2.8.8", - "ts-node": "10.9.2", + "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0" } diff --git a/packages/one-trust-notices/package.json b/packages/one-trust-notices/package.json index 73df7d6445..56e48cdff3 100644 --- a/packages/one-trust-notices/package.json +++ b/packages/one-trust-notices/package.json @@ -14,7 +14,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 ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5cf7ee4f7..3ca6b15a3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -210,9 +210,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1568,9 +1568,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -3487,10 +3487,6 @@ packages: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} @@ -3844,9 +3840,6 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -4481,18 +4474,6 @@ packages: '@tsconfig/node-lts@20.1.3': resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tsconfig/strictest@2.0.5': resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} @@ -4795,9 +4776,6 @@ packages: resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} engines: {node: '>= 10'} - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -5134,9 +5112,6 @@ packages: resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} engines: {node: '>=4.0.0'} - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -5264,10 +5239,6 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} @@ -6244,9 +6215,6 @@ packages: magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -7179,20 +7147,6 @@ packages: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} - ts-node@10.9.2: - 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 - ts-pattern@5.2.0: resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} @@ -7367,9 +7321,6 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -7543,10 +7494,6 @@ packages: yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -10145,10 +10092,6 @@ snapshots: '@colors/colors@1.6.0': {} - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 @@ -10373,11 +10316,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jsdevtools/ono@7.1.3': {} '@liuli-util/fs-extra@0.1.0': @@ -11335,14 +11273,6 @@ snapshots: '@tsconfig/node-lts@20.1.3': {} - '@tsconfig/node10@1.0.11': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - '@tsconfig/strictest@2.0.5': {} '@types/adm-zip@0.5.5': @@ -11719,8 +11649,6 @@ snapshots: tar-stream: 2.2.0 zip-stream: 4.1.1 - arg@4.1.3: {} - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -12140,8 +12068,6 @@ snapshots: dependencies: lodash.get: 4.4.2 - create-require@1.1.1: {} - cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -12245,8 +12171,6 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: {} - diff@5.2.0: {} dir-glob@3.0.1: @@ -13426,8 +13350,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - make-error@1.3.6: {} - media-typer@0.3.0: {} memory-pager@1.5.0: {} @@ -14450,24 +14372,6 @@ snapshots: triple-beam@1.4.1: {} - ts-node@10.9.2(@types/node@20.14.6)(typescript@5.4.5): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.6 - acorn: 8.12.1 - acorn-walk: 8.3.3 - 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 - ts-pattern@5.2.0: {} ts-toolbelt@9.6.0: {} @@ -14631,8 +14535,6 @@ snapshots: uuid@9.0.1: {} - v8-compile-cache-lib@3.0.1: {} - vary@1.1.2: {} vite-node@1.6.0(@types/node@20.14.6): @@ -14827,8 +14729,6 @@ snapshots: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - yn@3.1.1: {} - yocto-queue@0.1.0: {} yocto-queue@1.1.1: {} From 3d01766729e0af03d2d667186b89fa055d1484cd Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 6 Nov 2024 12:18:55 +0100 Subject: [PATCH 011/126] PIN-5494 - datalake interface exporter (#1145) --- ...dd a consumer Document to an Agreement.bru | 2 +- .../catalog/Add new e-service document.bru | 8 +- .../Publish the selected descriptor.bru | 6 +- docker/docker-compose.yml | 13 +- .../allow-list/allow-list.txt | 0 .../privacy-notices-path/latest/it/pp.json | 0 .../privacy-notices-path/latest/it/tos.json | 0 .../commons-test/src/containerTestUtils.ts | 5 +- packages/datalake-interface-exporter/.env | 17 +++ .../datalake-interface-exporter/.env.test | 12 ++ .../datalake-interface-exporter/Dockerfile | 45 +++++++ .../datalake-interface-exporter/README.md | 8 ++ .../aws.config.local | 4 + .../datalake-interface-exporter/package.json | 41 +++++++ .../src/config/config.ts | 31 +++++ .../datalake-interface-exporter/src/index.ts | 46 +++++++ .../src/interfaceExporter.ts | 45 +++++++ .../src/interfaceExporterV1.ts | 52 ++++++++ .../src/interfaceExporterV2.ts | 58 +++++++++ .../test/interfaceExporter.test.ts | 115 ++++++++++++++++++ .../test/tsconfig.json | 4 + .../datalake-interface-exporter/test/utils.ts | 10 ++ .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 ++ .../datalake-interface-exporter/tsconfig.json | 7 ++ .../vitest.config.ts | 11 ++ pnpm-lock.yaml | 49 ++++++++ 27 files changed, 582 insertions(+), 17 deletions(-) rename docker/minio-seed/{ => interop-local-bucket}/allow-list/allow-list.txt (100%) rename docker/minio-seed/{ => interop-local-bucket}/privacy-notices-path/latest/it/pp.json (100%) rename docker/minio-seed/{ => interop-local-bucket}/privacy-notices-path/latest/it/tos.json (100%) create mode 100644 packages/datalake-interface-exporter/.env create mode 100644 packages/datalake-interface-exporter/.env.test create mode 100644 packages/datalake-interface-exporter/Dockerfile create mode 100644 packages/datalake-interface-exporter/README.md create mode 100644 packages/datalake-interface-exporter/aws.config.local create mode 100644 packages/datalake-interface-exporter/package.json create mode 100644 packages/datalake-interface-exporter/src/config/config.ts create mode 100644 packages/datalake-interface-exporter/src/index.ts create mode 100644 packages/datalake-interface-exporter/src/interfaceExporter.ts create mode 100644 packages/datalake-interface-exporter/src/interfaceExporterV1.ts create mode 100644 packages/datalake-interface-exporter/src/interfaceExporterV2.ts create mode 100644 packages/datalake-interface-exporter/test/interfaceExporter.test.ts create mode 100644 packages/datalake-interface-exporter/test/tsconfig.json create mode 100644 packages/datalake-interface-exporter/test/utils.ts create mode 100644 packages/datalake-interface-exporter/test/vitestGlobalSetup.ts create mode 100644 packages/datalake-interface-exporter/tsconfig.check.json create mode 100644 packages/datalake-interface-exporter/tsconfig.json create mode 100644 packages/datalake-interface-exporter/vitest.config.ts diff --git a/collections/bff/agreements/Add a consumer Document to an Agreement.bru b/collections/bff/agreements/Add a consumer Document to an Agreement.bru index a46fcf2bf9..99cef3fefe 100644 --- a/collections/bff/agreements/Add a consumer Document to an Agreement.bru +++ b/collections/bff/agreements/Add a consumer Document to an Agreement.bru @@ -23,7 +23,7 @@ headers { body:multipart-form { name: testone 2 prettyName: testone 2 - doc: @file(/Users/ecamel/Downloads/Backend for Frontend Micro Service.postman_collection.json) + doc: @file() } docs { diff --git a/collections/bff/catalog/Add new e-service document.bru b/collections/bff/catalog/Add new e-service document.bru index 04c33c5deb..2e4eb12aba 100644 --- a/collections/bff/catalog/Add new e-service document.bru +++ b/collections/bff/catalog/Add new e-service document.bru @@ -11,8 +11,8 @@ post { } params:path { - eServiceId: 26f433e1-2c3c-4022-a14e-3c300baefc51 - descriptorId: abafb202-4f61-42bf-be2f-4efa5d4d0bc4 + eServiceId: {{eserviceId}} + descriptorId: {{descriptorId}} } headers { @@ -21,10 +21,8 @@ headers { Content-Type: multipart/form-data } - - body:multipart-form { kind: INTERFACE prettyName: asdasd - doc: @file(/Users/ecamel/Downloads/Interfaccia.yaml) + doc: @file() } diff --git a/collections/bff/catalog/Publish the selected descriptor.bru b/collections/bff/catalog/Publish the selected descriptor.bru index 948be07dcd..2210cb63e5 100644 --- a/collections/bff/catalog/Publish the selected descriptor.bru +++ b/collections/bff/catalog/Publish the selected descriptor.bru @@ -11,13 +11,11 @@ post { } params:path { - eServiceId: - descriptorId: + eServiceId: {{eserviceId}} + descriptorId: {{descriptorId}} } headers { Authorization: {{JWT}} x-correlation-id: {{correlation-id}} } - - diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8a84f69746..7f338ba38e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -152,11 +152,9 @@ services: # MinIO is a local S3-compatible storage, to replace AWS S3 in local development minio: - image: quay.io/minio/minio:RELEASE.2024-02-06T21-36-22Z - entrypoint: sh - command: > - -c 'mkdir -p /data/interop-local-bucket && - /usr/bin/minio server /data --console-address ":9001"' + image: minio/minio:RELEASE.2024-02-06T21-36-22Z + entrypoint: > + /usr/bin/minio server /data --console-address ':9001' ports: - 9000:9000 - 9001:9001 @@ -178,8 +176,11 @@ services: mc alias set minio http://minio:9000 testawskey testawssecret; mc ready minio; echo 'MinIO is ready. Seeding data...'; - mc cp --recursive data/ minio/interop-local-bucket/; + mc mb minio/interop-local-bucket || true; mc mb minio/interop-datalake-bucket || true; + mc mb minio/interop-application-import-export-local || true; + mc mb minio/interop-data-lake-interfaces-exports-local-es1 || true; + mc cp --recursive data/ minio/; " volumes: - ./minio-seed:/data diff --git a/docker/minio-seed/allow-list/allow-list.txt b/docker/minio-seed/interop-local-bucket/allow-list/allow-list.txt similarity index 100% rename from docker/minio-seed/allow-list/allow-list.txt rename to docker/minio-seed/interop-local-bucket/allow-list/allow-list.txt diff --git a/docker/minio-seed/privacy-notices-path/latest/it/pp.json b/docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/pp.json similarity index 100% rename from docker/minio-seed/privacy-notices-path/latest/it/pp.json rename to docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/pp.json diff --git a/docker/minio-seed/privacy-notices-path/latest/it/tos.json b/docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/tos.json similarity index 100% rename from docker/minio-seed/privacy-notices-path/latest/it/tos.json rename to docker/minio-seed/interop-local-bucket/privacy-notices-path/latest/it/tos.json diff --git a/packages/commons-test/src/containerTestUtils.ts b/packages/commons-test/src/containerTestUtils.ts index 3f76738c80..39a122d369 100644 --- a/packages/commons-test/src/containerTestUtils.ts +++ b/packages/commons-test/src/containerTestUtils.ts @@ -89,7 +89,10 @@ export const minioContainer = (config: S3Config): GenericContainer => }) .withEntrypoint(["sh", "-c"]) .withCommand([ - `mkdir -p /data/${config.s3Bucket} && /usr/bin/minio server /data`, + `mkdir -p /data/${config.s3Bucket} && + mkdir -p /data/test-bucket-1 && + mkdir -p /data/test-bucket-2 && + /usr/bin/minio server /data`, ]) .withExposedPorts(TEST_MINIO_PORT); diff --git a/packages/datalake-interface-exporter/.env b/packages/datalake-interface-exporter/.env new file mode 100644 index 0000000000..91570c3d23 --- /dev/null +++ b/packages/datalake-interface-exporter/.env @@ -0,0 +1,17 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="catalog" +KAFKA_GROUP_ID="catalog-group-local" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +CATALOG_TOPIC="event-store.catalog.events" + +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +AWS_REGION=eu-south-1 +AWS_CONFIG_FILE=aws.config.local + +ESERVICE_DOCUMENTS_S3_BUCKET=interop-local-bucket +DATALAKE_INTERFACES_EXPORT_S3_BUCKET=interop-data-lake-interfaces-exports-local-es1 diff --git a/packages/datalake-interface-exporter/.env.test b/packages/datalake-interface-exporter/.env.test new file mode 100644 index 0000000000..829c6c2d55 --- /dev/null +++ b/packages/datalake-interface-exporter/.env.test @@ -0,0 +1,12 @@ + + +# Needed by the test infrastructure to start the Minio testcontainer +LOG_LEVEL=info +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 +S3_BUCKET=test-bucket + +# We set these two to the names of the two test buckets created by default by the test infra +ESERVICE_DOCUMENTS_S3_BUCKET=test-bucket-1 +DATALAKE_INTERFACES_EXPORT_S3_BUCKET=test-bucket-2 diff --git a/packages/datalake-interface-exporter/Dockerfile b/packages/datalake-interface-exporter/Dockerfile new file mode 100644 index 0000000000..dcd696ea52 --- /dev/null +++ b/packages/datalake-interface-exporter/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/datalake-interface-exporter/package.json /app/packages/datalake-interface-exporter/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/datalake-interface-exporter /app/packages/datalake-interface-exporter +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/datalake-interface-exporter/node_modules \ + package*.json packages/datalake-interface-exporter/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/datalake-interface-exporter/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/datalake-interface-exporter +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/datalake-interface-exporter/README.md b/packages/datalake-interface-exporter/README.md new file mode 100644 index 0000000000..4dbfd20ccc --- /dev/null +++ b/packages/datalake-interface-exporter/README.md @@ -0,0 +1,8 @@ +# Datalake Interface Exporter + +This consumer: + +- listens for events that publish a new version of an eservice +- copies the new interface file for the new version to the target S3 bucket + +This target S3 bucket replicates the files to a bucket owned by Datalake. diff --git a/packages/datalake-interface-exporter/aws.config.local b/packages/datalake-interface-exporter/aws.config.local new file mode 100644 index 0000000000..34826a60e2 --- /dev/null +++ b/packages/datalake-interface-exporter/aws.config.local @@ -0,0 +1,4 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 diff --git a/packages/datalake-interface-exporter/package.json b/packages/datalake-interface-exporter/package.json new file mode 100644 index 0000000000..c206fe54a2 --- /dev/null +++ b/packages/datalake-interface-exporter/package.json @@ -0,0 +1,41 @@ +{ + "name": "pagopa-interop-datalake-interface-exporter", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability Datalake Eservice interface exporter", + "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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-commons-test": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/datalake-interface-exporter/src/config/config.ts b/packages/datalake-interface-exporter/src/config/config.ts new file mode 100644 index 0000000000..54bea1d948 --- /dev/null +++ b/packages/datalake-interface-exporter/src/config/config.ts @@ -0,0 +1,31 @@ +import { + CatalogTopicConfig, + FileManagerConfig, + KafkaConsumerConfig, + LoggerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const DatalakeInterfaceExporterConfig = LoggerConfig.and( + FileManagerConfig +) + .and(KafkaConsumerConfig) + .and(CatalogTopicConfig) + .and( + z + .object({ + ESERVICE_DOCUMENTS_S3_BUCKET: z.string(), + DATALAKE_INTERFACES_EXPORT_S3_BUCKET: z.string(), + }) + .transform((c) => ({ + eserviceDocumentsS3Bucket: c.ESERVICE_DOCUMENTS_S3_BUCKET, + datalakeInterfacesExportS3Bucket: + c.DATALAKE_INTERFACES_EXPORT_S3_BUCKET, + })) + ); + +export type DatalakeInterfaceExporterConfig = z.infer< + typeof DatalakeInterfaceExporterConfig +>; +export const config: DatalakeInterfaceExporterConfig = + DatalakeInterfaceExporterConfig.parse(process.env); diff --git a/packages/datalake-interface-exporter/src/index.ts b/packages/datalake-interface-exporter/src/index.ts new file mode 100644 index 0000000000..81dcc68b00 --- /dev/null +++ b/packages/datalake-interface-exporter/src/index.ts @@ -0,0 +1,46 @@ +/* eslint-disable functional/immutable-data */ +import { runConsumer } from "kafka-iam-auth"; +import { EachMessagePayload } from "kafkajs"; +import { + decodeKafkaMessage, + initFileManager, + logger, +} from "pagopa-interop-commons"; +import { + CorrelationId, + EServiceEvent, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { config } from "./config/config.js"; +import { exportInterfaceV1 } from "./interfaceExporterV1.js"; +import { exportInterfaceV2 } from "./interfaceExporterV2.js"; + +const fileManager = initFileManager(config); + +async function processMessage(payload: EachMessagePayload): Promise { + const decodedMsg = decodeKafkaMessage(payload.message, EServiceEvent); + const correlationId: CorrelationId = decodedMsg.correlation_id + ? unsafeBrandId(decodedMsg.correlation_id) + : generateId(); + + const loggerInstance = logger({ + serviceName: "datalake-interface-exporter", + eventType: decodedMsg.type, + eventVersion: decodedMsg.event_version, + streamId: decodedMsg.stream_id, + correlationId, + }); + + await match(decodedMsg) + .with({ event_version: 1 }, (msg) => + exportInterfaceV1(msg, payload, fileManager, loggerInstance) + ) + .with({ event_version: 2 }, (msg) => + exportInterfaceV2(msg, payload, fileManager, loggerInstance) + ) + .exhaustive(); +} + +await runConsumer(config, [config.catalogTopic], processMessage); diff --git a/packages/datalake-interface-exporter/src/interfaceExporter.ts b/packages/datalake-interface-exporter/src/interfaceExporter.ts new file mode 100644 index 0000000000..9afaae41df --- /dev/null +++ b/packages/datalake-interface-exporter/src/interfaceExporter.ts @@ -0,0 +1,45 @@ +import { FileManager, Logger } from "pagopa-interop-commons"; +import { + Descriptor, + EServiceId, + genericInternalError, +} from "pagopa-interop-models"; +import { config } from "./config/config.js"; + +export async function exportInterface( + eserviceId: EServiceId, + latestDescriptor: Descriptor, + fileManager: FileManager, + logger: Logger +): Promise { + logger.info( + `Exporting Interface for EService ${eserviceId} and Descriptor ${latestDescriptor.id}` + ); + + if (latestDescriptor.interface === undefined) { + throw genericInternalError( + `Published Descriptor ${latestDescriptor.id} does not have an interface` + ); + } + + const interfaceFile = await fileManager.get( + config.eserviceDocumentsS3Bucket, + latestDescriptor.interface.path, + logger + ); + + await fileManager.storeBytes( + { + bucket: config.datalakeInterfacesExportS3Bucket, + path: eserviceId, + resourceId: latestDescriptor.id, + name: latestDescriptor.interface.name, + content: Buffer.from(interfaceFile), + }, + logger + ); + + logger.info( + `Interface ${latestDescriptor.interface.name} for Eservice ${eserviceId} and Descriptor ${latestDescriptor.id} exported successfully` + ); +} diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV1.ts b/packages/datalake-interface-exporter/src/interfaceExporterV1.ts new file mode 100644 index 0000000000..466a4014b5 --- /dev/null +++ b/packages/datalake-interface-exporter/src/interfaceExporterV1.ts @@ -0,0 +1,52 @@ +import { FileManager, Logger } from "pagopa-interop-commons"; +import { + descriptorState, + EServiceEventEnvelopeV1, + fromDescriptorV1, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { EachMessagePayload } from "kafkajs"; +import { exportInterface } from "./interfaceExporter.js"; + +export async function exportInterfaceV1( + decodedMsg: EServiceEventEnvelopeV1, + originalPayload: EachMessagePayload, + fileManager: FileManager, + logger: Logger +): Promise { + await match(decodedMsg) + .with({ type: "EServiceDescriptorUpdated" }, async ({ data }) => { + if (data.eserviceDescriptor) { + logger.info( + `Processing ${decodedMsg.type} message - Partition number: ${originalPayload.partition} - Offset: ${originalPayload.message.offset}` + ); + const updatedDescriptor = fromDescriptorV1(data.eserviceDescriptor); + if (updatedDescriptor.state === descriptorState.published) { + await exportInterface( + unsafeBrandId(data.eserviceId), + updatedDescriptor, + fileManager, + logger + ); + } + } + }) + .with( + { type: "EServiceAdded" }, + { type: "ClonedEServiceAdded" }, + { type: "EServiceUpdated" }, + { type: "EServiceWithDescriptorsDeleted" }, + { type: "EServiceDocumentUpdated" }, + { type: "EServiceDeleted" }, + { type: "EServiceDocumentAdded" }, + { type: "EServiceDocumentDeleted" }, + { type: "EServiceDescriptorAdded" }, + { type: "MovedAttributesFromEserviceToDescriptors" }, + { type: "EServiceRiskAnalysisAdded" }, + { type: "EServiceRiskAnalysisUpdated" }, + { type: "EServiceRiskAnalysisDeleted" }, + () => undefined + ) + .exhaustive(); +} diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts new file mode 100644 index 0000000000..da5151180c --- /dev/null +++ b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts @@ -0,0 +1,58 @@ +import { EServiceEventEnvelopeV2, fromEServiceV2 } from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { FileManager, Logger } from "pagopa-interop-commons"; +import { EachMessagePayload } from "kafkajs"; +import { exportInterface } from "./interfaceExporter.js"; + +export async function exportInterfaceV2( + decodedMsg: EServiceEventEnvelopeV2, + originalPayload: EachMessagePayload, + fileManager: FileManager, + logger: Logger +): Promise { + await match(decodedMsg) + .with({ type: "EServiceDescriptorPublished" }, async ({ data }) => { + if (data.eservice) { + logger.info( + `Processing ${decodedMsg.type} message - Partition number: ${originalPayload.partition} - Offset: ${originalPayload.message.offset}` + ); + const eservice = fromEServiceV2(data.eservice); + const publishedDescriptor = eservice.descriptors.find( + (d) => d.id === data.descriptorId + ); + if (publishedDescriptor) { + await exportInterface( + eservice.id, + publishedDescriptor, + fileManager, + logger + ); + } + } + }) + .with( + { type: "EServiceAdded" }, + { type: "DraftEServiceUpdated" }, + { type: "EServiceDeleted" }, + { type: "EServiceCloned" }, + { type: "EServiceDescriptorAdded" }, + { type: "EServiceDraftDescriptorUpdated" }, + { type: "EServiceDescriptorQuotasUpdated" }, + { type: "EServiceDescriptorActivated" }, + { type: "EServiceDescriptorArchived" }, + { type: "EServiceDescriptorSuspended" }, + { type: "EServiceDraftDescriptorDeleted" }, + { type: "EServiceDescriptorInterfaceAdded" }, + { type: "EServiceDescriptorDocumentAdded" }, + { type: "EServiceDescriptorInterfaceUpdated" }, + { type: "EServiceDescriptorDocumentUpdated" }, + { type: "EServiceDescriptorInterfaceDeleted" }, + { type: "EServiceDescriptorDocumentDeleted" }, + { type: "EServiceRiskAnalysisAdded" }, + { type: "EServiceRiskAnalysisUpdated" }, + { type: "EServiceRiskAnalysisDeleted" }, + { type: "EServiceDescriptionUpdated" }, + () => undefined + ) + .exhaustive(); +} diff --git a/packages/datalake-interface-exporter/test/interfaceExporter.test.ts b/packages/datalake-interface-exporter/test/interfaceExporter.test.ts new file mode 100644 index 0000000000..1b466a6f5b --- /dev/null +++ b/packages/datalake-interface-exporter/test/interfaceExporter.test.ts @@ -0,0 +1,115 @@ +import { describe, expect, it } from "vitest"; +import { + getMockDescriptor, + getMockDocument, +} from "pagopa-interop-commons-test"; +import { + Descriptor, + EServiceDocumentId, + EServiceId, + generateId, +} from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "../src/config/config.js"; +import { exportInterface } from "../src/interfaceExporter.js"; +import { fileManager } from "./utils.js"; + +describe("interfaceExporter", () => { + it("should export interface files for a descriptor", async () => { + const mockEserviceId1 = generateId(); + const mockDocumentId1 = generateId(); + const mockDocument1 = { + ...getMockDocument(), + id: mockDocumentId1, + name: "document1.json", + path: `interop-eservice-documents/${mockDocumentId1}/document1.json`, + }; + const mockDescriptor1: Descriptor = { + ...getMockDescriptor(), + interface: mockDocument1, + }; + + const mockEserviceId2 = generateId(); + const mockDocumentId2 = generateId(); + const mockDocument2 = { + ...getMockDocument(), + id: mockDocumentId2, + name: "document2.txt", + path: `interop-eservice-documents/${mockDocumentId2}/document2.txt`, + }; + const mockDescriptor2: Descriptor = { + ...getMockDescriptor(), + interface: mockDocument2, + }; + + await fileManager.storeBytes( + { + bucket: config.eserviceDocumentsS3Bucket, + path: "interop-eservice-documents", + resourceId: mockDocument1.id, + name: mockDocument1.name, + content: Buffer.from("test-content"), + }, + genericLogger + ); + + await fileManager.storeBytes( + { + bucket: config.eserviceDocumentsS3Bucket, + path: "interop-eservice-documents", + resourceId: mockDocument2.id, + name: mockDocument2.name, + content: Buffer.from("test-content"), + }, + genericLogger + ); + + await exportInterface( + mockEserviceId1, + mockDescriptor1, + fileManager, + genericLogger + ); + + await exportInterface( + mockEserviceId2, + mockDescriptor2, + fileManager, + genericLogger + ); + + expect( + await fileManager.listFiles( + config.datalakeInterfacesExportS3Bucket, + genericLogger + ) + ).toContain( + `${mockEserviceId1}/${mockDescriptor1.id}/${mockDocument1.name}` + ); + + expect( + await fileManager.listFiles( + config.datalakeInterfacesExportS3Bucket, + genericLogger + ) + ).toContain( + `${mockEserviceId2}/${mockDescriptor2.id}/${mockDocument2.name}` + ); + }); + + it("should fail if descriptor does not have an interface", async () => { + const mockEserviceId = generateId(); + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + interface: undefined, + }; + await expect( + exportInterface( + mockEserviceId, + mockDescriptor, + fileManager, + genericLogger + ) + ).rejects.toThrow(); + }); +}); diff --git a/packages/datalake-interface-exporter/test/tsconfig.json b/packages/datalake-interface-exporter/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/datalake-interface-exporter/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/datalake-interface-exporter/test/utils.ts b/packages/datalake-interface-exporter/test/utils.ts new file mode 100644 index 0000000000..45cae9e84c --- /dev/null +++ b/packages/datalake-interface-exporter/test/utils.ts @@ -0,0 +1,10 @@ +import { setupTestContainersVitest } from "pagopa-interop-commons-test"; +import { afterEach, inject } from "vitest"; + +export const { cleanup, fileManager } = await setupTestContainersVitest( + undefined, + undefined, + inject("fileManagerConfig") +); + +afterEach(cleanup); diff --git a/packages/datalake-interface-exporter/test/vitestGlobalSetup.ts b/packages/datalake-interface-exporter/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/datalake-interface-exporter/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/datalake-interface-exporter/tsconfig.check.json b/packages/datalake-interface-exporter/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/datalake-interface-exporter/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/datalake-interface-exporter/tsconfig.json b/packages/datalake-interface-exporter/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/datalake-interface-exporter/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/datalake-interface-exporter/vitest.config.ts b/packages/datalake-interface-exporter/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/datalake-interface-exporter/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 3ca6b15a3d..a6da4af8b5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1535,6 +1535,55 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/datalake-interface-exporter: + dependencies: + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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-commons-test: + specifier: workspace:* + version: link:../commons-test + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/dtd-catalog-exporter: dependencies: dotenv-flow: From 3cbbd8071c3c6697ddd1d327683360aa3a069620 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 6 Nov 2024 13:04:21 +0100 Subject: [PATCH 012/126] IMN-793 Add scaffold for authorization-platformstate-writer (#1051) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../authorization-platformstate-writer/.env | 12 +++ .../Dockerfile | 45 +++++++++++ .../aws.config.local | 9 +++ .../package.json | 47 +++++++++++ .../src/config/config.ts | 15 ++++ .../src/consumerServiceV1.ts | 26 ++++++ .../src/consumerServiceV2.ts | 32 ++++++++ .../src/index.ts | 43 ++++++++++ .../test/sample.integration.test.ts | 46 +++++++++++ .../test/tsconfig.json | 4 + .../test/utils.ts | 3 + .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 ++ .../tsconfig.json | 9 +++ .../vitest.config.ts | 11 +++ packages/models/src/index.ts | 2 +- .../{dynamoDB-keys.ts => commons.ts} | 11 +++ .../platform-states-entry.ts | 3 + .../token-generation-states-entry.ts | 11 +-- pnpm-lock.yaml | 81 ++++++++++++++++++- 20 files changed, 408 insertions(+), 12 deletions(-) create mode 100644 packages/authorization-platformstate-writer/.env create mode 100644 packages/authorization-platformstate-writer/Dockerfile create mode 100644 packages/authorization-platformstate-writer/aws.config.local create mode 100644 packages/authorization-platformstate-writer/package.json create mode 100644 packages/authorization-platformstate-writer/src/config/config.ts create mode 100644 packages/authorization-platformstate-writer/src/consumerServiceV1.ts create mode 100644 packages/authorization-platformstate-writer/src/consumerServiceV2.ts create mode 100644 packages/authorization-platformstate-writer/src/index.ts create mode 100644 packages/authorization-platformstate-writer/test/sample.integration.test.ts create mode 100644 packages/authorization-platformstate-writer/test/tsconfig.json create mode 100644 packages/authorization-platformstate-writer/test/utils.ts create mode 100644 packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts create mode 100644 packages/authorization-platformstate-writer/tsconfig.check.json create mode 100644 packages/authorization-platformstate-writer/tsconfig.json create mode 100644 packages/authorization-platformstate-writer/vitest.config.ts rename packages/models/src/token-generation-readmodel/{dynamoDB-keys.ts => commons.ts} (88%) diff --git a/packages/authorization-platformstate-writer/.env b/packages/authorization-platformstate-writer/.env new file mode 100644 index 0000000000..def5bafc7f --- /dev/null +++ b/packages/authorization-platformstate-writer/.env @@ -0,0 +1,12 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="authorization" +KAFKA_GROUP_ID="authorization-group-local" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +AUTHORIZATION_TOPIC="event-store.authorization.events" +AWS_CONFIG_FILE=aws.config.local +TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" +TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" + +AWS_REGION="eu-south-1" diff --git a/packages/authorization-platformstate-writer/Dockerfile b/packages/authorization-platformstate-writer/Dockerfile new file mode 100644 index 0000000000..9d527f08bb --- /dev/null +++ b/packages/authorization-platformstate-writer/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/authorization-platformstate-writer/package.json /app/packages/authorization-platformstate-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/authorization-platformstate-writer /app/packages/authorization-platformstate-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/authorization-platformstate-writer/node_modules \ + package*.json packages/authorization-platformstate-writer/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/authorization-platformstate-writer/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/authorization-platformstate-writer +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/authorization-platformstate-writer/aws.config.local b/packages/authorization-platformstate-writer/aws.config.local new file mode 100644 index 0000000000..f3016f81d6 --- /dev/null +++ b/packages/authorization-platformstate-writer/aws.config.local @@ -0,0 +1,9 @@ +[default] +aws_access_key_id=key +aws_secret_access_key=secret +region=eu-south-1 +services=local + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 diff --git a/packages/authorization-platformstate-writer/package.json b/packages/authorization-platformstate-writer/package.json new file mode 100644 index 0000000000..6106ef0a72 --- /dev/null +++ b/packages/authorization-platformstate-writer/package.json @@ -0,0 +1,47 @@ +{ + "name": "pagopa-interop-authorization-platformstate-writer", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability authorization consumer service that updates the token-generation-read-model", + "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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "@types/uuid": "9.0.8", + "date-fns": "3.6.0", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "tsx": "4.19.1", + "typescript": "5.4.5", + "uuid": "10.0.0", + "vitest": "1.6.0" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "3.637.0", + "@aws-sdk/util-dynamodb": "3.637.0", + "@protobuf-ts/runtime": "2.9.4", + "connection-string": "4.4.0", + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/authorization-platformstate-writer/src/config/config.ts b/packages/authorization-platformstate-writer/src/config/config.ts new file mode 100644 index 0000000000..3258ca8188 --- /dev/null +++ b/packages/authorization-platformstate-writer/src/config/config.ts @@ -0,0 +1,15 @@ +import { + AuthorizationTopicConfig, + PlatformStateWriterConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const AuthorizationPlatformStateWriterConfig = + PlatformStateWriterConfig.and(AuthorizationTopicConfig); + +export type AuthorizationPlatformStateWriterConfig = z.infer< + typeof AuthorizationPlatformStateWriterConfig +>; + +export const config: AuthorizationPlatformStateWriterConfig = + AuthorizationPlatformStateWriterConfig.parse(process.env); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts new file mode 100644 index 0000000000..78b5d36e02 --- /dev/null +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -0,0 +1,26 @@ +import { match } from "ts-pattern"; +import { AuthorizationEventEnvelopeV1 } from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; + +export async function handleMessageV1( + message: AuthorizationEventEnvelopeV1, + _dynamoDBClient: DynamoDBClient +): Promise { + await match(message) + .with( + { type: "ClientAdded" }, + { type: "ClientDeleted" }, + { type: "ClientPurposeAdded" }, + { type: "ClientPurposeRemoved" }, + { type: "KeyDeleted" }, + { type: "KeyRelationshipToUserMigrated" }, + { type: "KeysAdded" }, + { type: "RelationshipAdded" }, + { type: "RelationshipAdded" }, + { type: "RelationshipRemoved" }, + { type: "UserAdded" }, + { type: "UserRemoved" }, + async () => Promise.resolve() + ) + .exhaustive(); +} diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts new file mode 100644 index 0000000000..4ad0fd25ab --- /dev/null +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -0,0 +1,32 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { AuthorizationEventEnvelopeV2 } from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export async function handleMessageV2( + message: AuthorizationEventEnvelopeV2, + _dynamoDBClient: DynamoDBClient +): Promise { + await match(message) + .with( + { type: "ClientAdded" }, + { type: "ClientDeleted" }, + { type: "ClientKeyAdded" }, + { type: "ClientKeyDeleted" }, + { type: "ClientPurposeAdded" }, + { type: "ClientPurposeRemoved" }, + { type: "ClientUserAdded" }, + { type: "ClientUserDeleted" }, + { type: "ProducerKeychainAdded" }, + { type: "ProducerKeychainDeleted" }, + { type: "ProducerKeychainEServiceAdded" }, + { type: "ProducerKeychainEServiceRemoved" }, + { type: "ProducerKeychainEServiceAdded" }, + { type: "ProducerKeychainEServiceRemoved" }, + { type: "ProducerKeychainKeyAdded" }, + { type: "ProducerKeychainKeyDeleted" }, + { type: "ProducerKeychainUserAdded" }, + { type: "ProducerKeychainUserDeleted" }, + () => Promise.resolve() + ) + .exhaustive(); +} diff --git a/packages/authorization-platformstate-writer/src/index.ts b/packages/authorization-platformstate-writer/src/index.ts new file mode 100644 index 0000000000..661f322d2d --- /dev/null +++ b/packages/authorization-platformstate-writer/src/index.ts @@ -0,0 +1,43 @@ +import { EachMessagePayload } from "kafkajs"; +import { logger, decodeKafkaMessage } from "pagopa-interop-commons"; +import { runConsumer } from "kafka-iam-auth"; +import { + AuthorizationEvent, + CorrelationId, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { handleMessageV1 } from "./consumerServiceV1.js"; +import { handleMessageV2 } from "./consumerServiceV2.js"; +import { config } from "./config/config.js"; + +const dynamoDBClient = new DynamoDBClient({}); +async function processMessage({ + message, + partition, +}: EachMessagePayload): Promise { + const decodedMessage = decodeKafkaMessage(message, AuthorizationEvent); + + const loggerInstance = logger({ + serviceName: "authorization-platformstate-writer", + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + correlationId: decodedMessage.correlation_id + ? unsafeBrandId(decodedMessage.correlation_id) + : generateId(), + }); + + await match(decodedMessage) + .with({ event_version: 1 }, (msg) => handleMessageV1(msg, dynamoDBClient)) + .with({ event_version: 2 }, (msg) => handleMessageV2(msg, dynamoDBClient)) + .exhaustive(); + + loggerInstance.info( + `Token-generation read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + ); +} + +await runConsumer(config, [config.authorizationTopic], processMessage); diff --git a/packages/authorization-platformstate-writer/test/sample.integration.test.ts b/packages/authorization-platformstate-writer/test/sample.integration.test.ts new file mode 100644 index 0000000000..a4e96fa400 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/sample.integration.test.ts @@ -0,0 +1,46 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, +} from "pagopa-interop-commons-test"; +import { config } from "./utils.js"; + +describe("integration tests V2 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + it("sample", () => { + expect(1).toBe(1); + }); +}); diff --git a/packages/authorization-platformstate-writer/test/tsconfig.json b/packages/authorization-platformstate-writer/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/authorization-platformstate-writer/test/utils.ts b/packages/authorization-platformstate-writer/test/utils.ts new file mode 100644 index 0000000000..aca07c9cc6 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/utils.ts @@ -0,0 +1,3 @@ +import { inject } from "vitest"; + +export const config = inject("tokenGenerationReadModelConfig"); diff --git a/packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts b/packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..85a4c8ea41 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test/index.js"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/authorization-platformstate-writer/tsconfig.check.json b/packages/authorization-platformstate-writer/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/authorization-platformstate-writer/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/authorization-platformstate-writer/tsconfig.json b/packages/authorization-platformstate-writer/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/authorization-platformstate-writer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/authorization-platformstate-writer/vitest.config.ts b/packages/authorization-platformstate-writer/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/authorization-platformstate-writer/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/packages/models/src/index.ts b/packages/models/src/index.ts index 930036a35a..853c28d655 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -56,7 +56,7 @@ export * from "./user/user.js"; export * from "./token-generation-readmodel/platform-states-entry.js"; export * from "./token-generation-readmodel/token-generation-states-entry.js"; -export * from "./token-generation-readmodel/dynamoDB-keys.js"; +export * from "./token-generation-readmodel/commons.js"; // Protobuf export * from "./protobuf/protobuf.js"; diff --git a/packages/models/src/token-generation-readmodel/dynamoDB-keys.ts b/packages/models/src/token-generation-readmodel/commons.ts similarity index 88% rename from packages/models/src/token-generation-readmodel/dynamoDB-keys.ts rename to packages/models/src/token-generation-readmodel/commons.ts index 184c039d5d..86308beab5 100644 --- a/packages/models/src/token-generation-readmodel/dynamoDB-keys.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -1,3 +1,4 @@ +import { z } from "zod"; import { EServiceId, DescriptorId, @@ -93,3 +94,13 @@ export const makeGSIPKClientIdPurposeId = ({ purposeId: PurposeId; }): GSIPKClientIdPurposeId => unsafeBrandId(`${clientId}#${purposeId}`); + +export const clientKindTokenStates = { + consumer: "CONSUMER", + api: "API", +} as const; +export const ClientKindTokenStates = z.enum([ + Object.values(clientKindTokenStates)[0], + ...Object.values(clientKindTokenStates).slice(1), +]); +export type ClientKindTokenStates = z.infer; diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index 3c0c507435..e12c8ae6ec 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -11,6 +11,7 @@ import { PurposeVersionId, TenantId, } from "../brandedIds.js"; +import { ClientKindTokenStates } from "./commons.js"; export const itemState = { active: "ACTIVE", @@ -60,6 +61,8 @@ export type PlatformStatesAgreementEntry = z.infer< export const PlatformStatesClientEntry = PlatformStatesBaseEntry.extend({ PK: PlatformStatesClientPK, + clientKind: ClientKindTokenStates, + clientConsumerId: TenantId, clientPurposesIds: z.array(PurposeId), }); export type PlatformStatesClientEntry = z.infer< diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index 2ec202ccd7..db65a91e19 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -12,16 +12,7 @@ import { TokenGenerationStatesClientKidPurposePK, } from "../brandedIds.js"; import { ItemState } from "./platform-states-entry.js"; - -export const clientKindTokenStates = { - consumer: "CONSUMER", - api: "API", -} as const; -export const ClientKindTokenStates = z.enum([ - Object.values(clientKindTokenStates)[0], - ...Object.values(clientKindTokenStates).slice(1), -]); -export type ClientKindTokenStates = z.infer; +import { ClientKindTokenStates } from "./commons.js"; const TokenGenerationStatesBaseEntry = z.object({ consumerId: TenantId, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6da4af8b5..8373799962 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -674,6 +674,73 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/authorization-platformstate-writer: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.637.0 + version: 3.637.0 + '@aws-sdk/util-dynamodb': + specifier: 3.637.0 + version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + uuid: + specifier: 10.0.0 + version: 10.0.0 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/authorization-process: dependencies: '@zodios/core': @@ -4640,6 +4707,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -5554,6 +5624,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@3.0.0-alpha-1: @@ -7366,6 +7437,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -9429,7 +9504,7 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.1.3 + '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 @@ -11460,6 +11535,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/uuid@9.0.8': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -14582,6 +14659,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@9.0.1: {} vary@1.1.2: {} From d4d8f54f96bddb049086dd7e52b842e8dddfb707 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 6 Nov 2024 14:21:43 +0100 Subject: [PATCH 013/126] PIN-5566 - Fixing DTD catalog exporter termination and package.json (#1166) --- packages/dtd-catalog-exporter/package.json | 7 +++---- packages/dtd-catalog-exporter/src/index.ts | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 6d8ea31ecd..48dac47f26 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -10,10 +10,9 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "check": "tsc --project tsconfig.check.json", - "start:migrate": "tsx -r 'dotenv-flow/config' ./src/index.ts", - "start:verify": "tsx -r 'dotenv-flow/config' ./src/read-models-migration-check.ts", - "build": "tsc" + "start": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" }, "keywords": [], "author": "", diff --git a/packages/dtd-catalog-exporter/src/index.ts b/packages/dtd-catalog-exporter/src/index.ts index ea2659fc04..f3150907ee 100644 --- a/packages/dtd-catalog-exporter/src/index.ts +++ b/packages/dtd-catalog-exporter/src/index.ts @@ -16,3 +16,8 @@ await dtdCatalogExporterServiceBuilder({ correlationId: generateId(), }), }).exportDtdPublicCatalog(); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. From b34486279d0c04efd5c99d6bfbf84e54e615a0a7 Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:53:05 +0100 Subject: [PATCH 014/126] Fix one trust notices (#1167) --- packages/one-trust-notices/.env | 2 ++ packages/one-trust-notices/package.json | 2 +- .../src/services/html2json.ts | 2 +- packages/pn-consumers/.env | 4 +++- pnpm-lock.yaml | 19 +++++++++++++------ 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/one-trust-notices/.env b/packages/one-trust-notices/.env index 34a132a77e..94054af2e8 100644 --- a/packages/one-trust-notices/.env +++ b/packages/one-trust-notices/.env @@ -1,3 +1,5 @@ +LOG_LEVEL=info + AWS_REGION=eu-south-1 LANGS=it,de,fr,es diff --git a/packages/one-trust-notices/package.json b/packages/one-trust-notices/package.json index 56e48cdff3..8e263e39ae 100644 --- a/packages/one-trust-notices/package.json +++ b/packages/one-trust-notices/package.json @@ -21,7 +21,7 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/html2json": "1.0.1", - "@types/lodash": "4.14.196", + "@types/lodash.isempty": "4.4.9", "@types/node": "20.4.9", "prettier": "2.8.8", "tsx": "4.19.1", diff --git a/packages/one-trust-notices/src/services/html2json.ts b/packages/one-trust-notices/src/services/html2json.ts index fa78126b13..fbbb4ce6df 100644 --- a/packages/one-trust-notices/src/services/html2json.ts +++ b/packages/one-trust-notices/src/services/html2json.ts @@ -2,7 +2,7 @@ /* eslint-disable fp/no-delete */ import h2j from "html2json"; -import isEmpty from "lodash/isEmpty.js"; +import isEmpty from "lodash.isempty"; export function html2json(html: string): h2j.Node { const jsonHtlmNodes = h2j.html2json(html); diff --git a/packages/pn-consumers/.env b/packages/pn-consumers/.env index 7f4a870b38..b47ddc4580 100644 --- a/packages/pn-consumers/.env +++ b/packages/pn-consumers/.env @@ -1,5 +1,7 @@ LOG_LEVEL=info +AWS_REGION=eu-south-1 + READMODEL_DB_HOST="localhost" READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" @@ -7,7 +9,7 @@ READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 MAIL_RECIPIENTS="" -REPORT_SENDER_MAIL="" +REPORT_SENDER_MAIL="test@test.it" REPORT_SENDER_LABEL="" PN_ESERVICE_ID="" COMUNI_E_LORO_CONSORZI_E_ASSOCIAZIONI_ATTRIBUTE_ID="" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8373799962..a9841b24c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2094,9 +2094,9 @@ importers: '@types/html2json': specifier: 1.0.1 version: 1.0.1 - '@types/lodash': - specifier: 4.14.196 - version: 4.14.196 + '@types/lodash.isempty': + specifier: 4.4.9 + version: 4.4.9 '@types/node': specifier: 20.4.9 version: 20.4.9 @@ -4641,11 +4641,14 @@ packages: '@types/jsonwebtoken@9.0.6': resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + '@types/lodash.isempty@4.4.9': + resolution: {integrity: sha512-DPSFfnT2JmZiAWNWOU8IRZws/Ha6zyGF5m06TydfsY+0dVoQqby2J61Na2QU4YtwiZ+moC6cJS6zWYBJq4wBVw==} + '@types/lodash.isequal@4.5.8': resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} - '@types/lodash@4.14.196': - resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} + '@types/lodash@4.17.13': + resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} '@types/lodash@4.17.6': resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} @@ -11461,11 +11464,15 @@ snapshots: dependencies: '@types/node': 20.14.6 + '@types/lodash.isempty@4.4.9': + dependencies: + '@types/lodash': 4.17.13 + '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - '@types/lodash@4.14.196': {} + '@types/lodash@4.17.13': {} '@types/lodash@4.17.6': {} From 3c71dd3fe34c393b7069426a84b0addf6563b658 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 6 Nov 2024 17:54:18 +0100 Subject: [PATCH 015/126] IMN-795 Add events service v2 in authorization-platformstate-writer (#1062) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../package.json | 2 - .../src/consumerServiceV2.ts | 391 ++- .../src/utils.ts | 1121 +++++++ .../consumerServiceV2.integration.test.ts | 2768 +++++++++++++++++ .../test/utils.test.ts | 779 +++++ packages/commons-test/src/testUtils.ts | 49 +- .../src/tokenGenerationReadmodelUtils.ts | 111 +- packages/models/src/brandedIds.ts | 14 +- .../src/token-generation-readmodel/commons.ts | 4 + .../platform-states-entry.ts | 9 + .../token-generation-states-entry.ts | 11 +- pnpm-lock.yaml | 17 - 12 files changed, 5238 insertions(+), 38 deletions(-) create mode 100644 packages/authorization-platformstate-writer/src/utils.ts create mode 100644 packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts create mode 100644 packages/authorization-platformstate-writer/test/utils.test.ts diff --git a/packages/authorization-platformstate-writer/package.json b/packages/authorization-platformstate-writer/package.json index 6106ef0a72..62ee6846f6 100644 --- a/packages/authorization-platformstate-writer/package.json +++ b/packages/authorization-platformstate-writer/package.json @@ -22,13 +22,11 @@ "devDependencies": { "@pagopa/eslint-config": "3.0.0", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "date-fns": "3.6.0", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "tsx": "4.19.1", "typescript": "5.4.5", - "uuid": "10.0.0", "vitest": "1.6.0" }, "dependencies": { diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 4ad0fd25ab..4fd99439fa 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -1,19 +1,385 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { AuthorizationEventEnvelopeV2 } from "pagopa-interop-models"; +import { + AuthorizationEventEnvelopeV2, + Client, + ClientV2, + fromClientV2, + genericInternalError, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesClientPK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + missingKafkaMessageDataError, + PlatformStatesClientEntry, + PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + unsafeBrandId, +} from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { + clientKindToTokenGenerationStatesClientKind, + convertEntriesToClientKidInTokenGenerationStates, + deleteClientEntryFromPlatformStates, + deleteEntriesFromTokenStatesByClient, + deleteEntriesFromTokenStatesByKid, + deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + readClientEntry, + readClientEntriesInTokenGenerationStates, + cleanClientPurposeIdsInPlatformStatesEntry, + deleteClientEntryFromTokenGenerationStatesTable, + extractKidFromTokenEntryPK, + extractAgreementIdFromAgreementPK, + retrievePlatformStatesByPurpose, + upsertPlatformClientEntry, + upsertTokenClientKidEntry, + upsertTokenStateClientPurposeEntry, + updateTokenDataForSecondRetrieval, + createTokenClientPurposeEntry, +} from "./utils.js"; export async function handleMessageV2( message: AuthorizationEventEnvelopeV2, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "ClientKeyAdded" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + + const pem = client.keys.find( + (key) => key.kid === msg.data.kid + )?.encodedPem; + if (!pem) { + throw missingKafkaMessageDataError("key", msg.type); + } + + const platformClientPK = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + // platform-states + const platformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + } + + // token-generation-states + if (client.purposes.length > 0) { + const addedEntries = await Promise.all( + client.purposes.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: msg.data.kid, + purposeId, + }); + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + PK: tokenClientKidPurposePK, + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind( + client.kind + ), + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(msg.data.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...((purposeEntry && { + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + }) || + {}), + ...((purposeEntry && + agreementEntry && { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + }) || + {}), + ...((catalogEntry && { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + }) || + {}), + }; + + await upsertTokenStateClientPurposeEntry( + clientKidPurposeEntry, + dynamoDBClient + ); + return clientKidPurposeEntry; + }) + ); + + // Second check for updated fields + await Promise.all( + client.purposes.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const addedClientKidPurposeEntry = addedEntries[index]; + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry: addedClientKidPurposeEntry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } else { + const clientKidEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: msg.data.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: pem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(msg.data.kid), + updatedAt: new Date().toISOString(), + }; + await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + } + }) + .with({ type: "ClientKeyDeleted" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + const pk = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const platformClientEntry: PlatformStatesClientEntry = { + PK: pk, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + } + + const GSIPK_kid = makeGSIPKKid(msg.data.kid); + await deleteEntriesFromTokenStatesByKid(GSIPK_kid, dynamoDBClient); + }) + .with({ type: "ClientPurposeAdded" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + + // platform-states + const platformClientEntryPK = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry( + platformClientEntryPK, + dynamoDBClient + ); + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + // platform-states + const platformClientEntry: PlatformStatesClientEntry = { + PK: platformClientEntryPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + } + + // token-generation-states + const GSIPK_clientId = client.id; + const tokenClientEntries = await readClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + if (tokenClientEntries.length === 0) { + return Promise.resolve(); + } else { + const purposeId = unsafeBrandId(msg.data.purposeId); + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const seenKids = new Set(); + const addedTokenClientPurposeEntries = await Promise.all( + tokenClientEntries.map(async (entry) => { + const parsedTokenClientEntry = + TokenGenerationStatesClientEntry.safeParse(entry); + const parsedTokenClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.safeParse(entry); + + if (parsedTokenClientEntry.success) { + const newTokenClientPurposeEntry = createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientEntry.data, + kid: extractKidFromTokenEntryPK(parsedTokenClientEntry.data.PK), + clientId: client.id, + consumerId: client.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + return newTokenClientPurposeEntry; + } + + if (parsedTokenClientPurposeEntry.success) { + const kid = extractKidFromTokenEntryPK( + parsedTokenClientPurposeEntry.data.PK + ); + if (!seenKids.has(kid)) { + const newTokenClientPurposeEntry = + createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientPurposeEntry.data, + kid, + clientId: client.id, + consumerId: client.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + seenKids.add(kid); + return newTokenClientPurposeEntry; + } + } + + throw genericInternalError(`Unable to parse ${entry}`); + }) + ); + + // Second check for updated fields + await Promise.all( + addedTokenClientPurposeEntries.map(async (entry) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } + }) + .with({ type: "ClientPurposeRemoved" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + const pk = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry) { + if (clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: unsafeBrandId(msg.data.purposeId), + }); + + // platform-states + await cleanClientPurposeIdsInPlatformStatesEntry( + pk, + msg.version, + dynamoDBClient + ); + + // token-generation-states + if (client.purposes.length > 0) { + await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } else { + await convertEntriesToClientKidInTokenGenerationStates( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } + } + } + }) + .with({ type: "ClientDeleted" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + const pk = makePlatformStatesClientPK(client.id); + await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); + + const GSIPK_clientId = client.id; + await deleteEntriesFromTokenStatesByClient( + GSIPK_clientId, + dynamoDBClient + ); + }) .with( { type: "ClientAdded" }, - { type: "ClientDeleted" }, - { type: "ClientKeyAdded" }, - { type: "ClientKeyDeleted" }, - { type: "ClientPurposeAdded" }, - { type: "ClientPurposeRemoved" }, { type: "ClientUserAdded" }, { type: "ClientUserDeleted" }, { type: "ProducerKeychainAdded" }, @@ -30,3 +396,14 @@ export async function handleMessageV2( ) .exhaustive(); } + +const parseClient = ( + clientV2: ClientV2 | undefined, + eventType: string +): Client => { + if (!clientV2) { + throw missingKafkaMessageDataError("client", eventType); + } + + return fromClientV2(clientV2); +}; diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts new file mode 100644 index 0000000000..9659d82c7c --- /dev/null +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -0,0 +1,1121 @@ +import { + AttributeValue, + DeleteItemCommand, + DeleteItemInput, + DynamoDBClient, + GetItemCommand, + GetItemCommandOutput, + GetItemInput, + PutItemCommand, + PutItemInput, + QueryCommand, + QueryCommandOutput, + QueryInput, +} from "@aws-sdk/client-dynamodb"; +import { + AgreementId, + ClientId, + clientKind, + ClientKind, + clientKindTokenStates, + ClientKindTokenStates, + genericInternalError, + GSIPKClientIdPurposeId, + GSIPKConsumerIdEServiceId, + GSIPKKid, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesAgreementPK, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesClientPK, + PlatformStatesEServiceDescriptorPK, + PlatformStatesPurposeEntry, + PlatformStatesPurposePK, + PurposeId, + TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericEntry, +} from "pagopa-interop-models"; +import { z } from "zod"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { match } from "ts-pattern"; +import { UpdateItemInput } from "@aws-sdk/client-dynamodb"; +import { UpdateItemCommand } from "@aws-sdk/client-dynamodb"; +import { config } from "./config/config.js"; + +export const deleteEntriesFromTokenStatesByKid = async ( + GSIPK_kid: GSIPKKid, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_kid: GSIPKKid, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Kid", + KeyConditionExpression: `GSIPK_kid = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_kid }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + for (const entry of tokenStateEntries.data) { + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (data.LastEvaluatedKey) { + await runPaginatedQuery( + GSIPK_kid, + dynamoDBClient, + data.LastEvaluatedKey + ); + } + } + }; + + await runPaginatedQuery(GSIPK_kid, dynamoDBClient, undefined); +}; + +export const deleteClientEntryFromPlatformStates = async ( + pk: PlatformStatesClientPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: DeleteItemInput = { + Key: { + PK: { S: pk }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new DeleteItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const deleteEntriesFromTokenStatesByClient = async ( + GSIPK_client: ClientId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_client: ClientId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Client", + KeyConditionExpression: `GSIPK_clientId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_client }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + for (const entry of tokenStateEntries.data) { + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (data.LastEvaluatedKey) { + await runPaginatedQuery( + GSIPK_client, + dynamoDBClient, + data.LastEvaluatedKey + ); + } + } + }; + + await runPaginatedQuery(GSIPK_client, dynamoDBClient, undefined); +}; + +export const deleteClientEntryFromTokenGenerationStatesTable = async ( + entryToDelete: TokenGenerationStatesGenericEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: DeleteItemInput = { + Key: { + PK: { S: entryToDelete.PK }, + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new DeleteItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const readClientEntry = async ( + primaryKey: PlatformStatesClientPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const clientEntry = PlatformStatesClientEntry.safeParse(unmarshalled); + + if (!clientEntry.success) { + throw genericInternalError( + `Unable to parse client entry item: result ${JSON.stringify( + clientEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return clientEntry.data; + } +}; + +const readTokenStateEntriesByGSIPKClientPurpose = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record +): Promise<{ + tokenStateEntries: TokenGenerationStatesClientPurposeEntry[]; + lastEvaluatedKey: Record | undefined; +}> => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "ClientPurpose", + KeyConditionExpression: `GSIPK_clientId_purposeId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_clientId_purposeId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + return { + tokenStateEntries: tokenStateEntries.data, + lastEvaluatedKey: data.LastEvaluatedKey, + }; + } +}; + +export const deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable = + async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient + ): Promise => { + const runPaginatedQuery = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const res = await readTokenStateEntriesByGSIPKClientPurpose( + GSIPK_clientId_purposeId, + dynamoDBClient, + exclusiveStartKey + ); + + for (const entry of res.tokenStateEntries) { + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (res.lastEvaluatedKey) { + await runPaginatedQuery( + GSIPK_clientId_purposeId, + dynamoDBClient, + res.lastEvaluatedKey + ); + } + }; + await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); + }; + +export const convertEntriesToClientKidInTokenGenerationStates = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const res = await readTokenStateEntriesByGSIPKClientPurpose( + GSIPK_clientId_purposeId, + dynamoDBClient, + exclusiveStartKey + ); + + // convert entries + for (const entry of res.tokenStateEntries) { + const newEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: entry.GSIPK_clientId, + kid: entry.GSIPK_kid, + }), + consumerId: entry.consumerId, + clientKind: entry.clientKind, + publicKey: entry.publicKey, + GSIPK_clientId: entry.GSIPK_clientId, + GSIPK_kid: entry.GSIPK_kid, + updatedAt: new Date().toISOString(), + }; + + // write the new one + await writeTokenStateClientEntry(newEntry, dynamoDBClient); + + // delete the old one + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + } + + if (!res.lastEvaluatedKey) { + return res.tokenStateEntries; + } else { + return [ + ...res.tokenStateEntries, + ...(await runPaginatedQuery( + GSIPK_clientId_purposeId, + dynamoDBClient, + res.lastEvaluatedKey + )), + ]; + } + }; + await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); +}; + +export const writeTokenStateClientEntry = async ( + tokenStateEntry: TokenGenerationStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: tokenStateEntry.PK, + }, + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const readCatalogEntry = async ( + primaryKey: PlatformStatesEServiceDescriptorPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const catalogEntry = PlatformStatesCatalogEntry.safeParse(unmarshalled); + + if (!catalogEntry.success) { + throw genericInternalError( + `Unable to parse catalog entry item: result ${JSON.stringify( + catalogEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return catalogEntry.data; + } +}; + +export const readPlatformAgreementEntryByGSIPKConsumerIdEServiceId = async ( + gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNamePlatform, + IndexName: "Agreement", + KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: gsiPKConsumerIdEServiceId }, + }, + ScanIndexForward: false, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + return undefined; + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + const platformAgreementEntries = z + .array(PlatformStatesAgreementEntry) + .safeParse(unmarshalledItems); + + if (platformAgreementEntries.success) { + return platformAgreementEntries.data[0]; + } else { + throw genericInternalError( + `Unable to parse platform agreement entries: result ${JSON.stringify( + platformAgreementEntries + )} ` + ); + } + } +}; + +export const readPlatformPurposeEntry = async ( + primaryKey: PlatformStatesPurposePK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + return undefined; + } else { + const unmarshalled = unmarshall(data.Item); + const purposeEntry = PlatformStatesPurposeEntry.safeParse(unmarshalled); + + if (!purposeEntry.success) { + throw genericInternalError( + `Unable to parse purpose entry item: result ${JSON.stringify( + purposeEntry + )} - data ${JSON.stringify(data)} ` + ); + } + return purposeEntry.data; + } +}; + +export const upsertTokenStateClientPurposeEntry = async ( + tokenStateEntry: TokenGenerationStatesClientPurposeEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + Item: { + PK: { + S: tokenStateEntry.PK, + }, + ...(tokenStateEntry.descriptorState + ? { + descriptorState: { + S: tokenStateEntry.descriptorState, + }, + } + : {}), + ...(tokenStateEntry.descriptorAudience + ? { + descriptorAudience: { + L: tokenStateEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + } + : {}), + ...(tokenStateEntry.descriptorVoucherLifespan + ? { + descriptorVoucherLifespan: { + N: tokenStateEntry.descriptorVoucherLifespan.toString(), + }, + } + : {}), + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + ...(tokenStateEntry.agreementId + ? { + agreementId: { + S: tokenStateEntry.agreementId, + }, + } + : {}), + ...(tokenStateEntry.purposeVersionId + ? { + purposeVersionId: { + S: tokenStateEntry.purposeVersionId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_consumerId_eserviceId + ? { + GSIPK_consumerId_eserviceId: { + S: tokenStateEntry.GSIPK_consumerId_eserviceId, + }, + } + : {}), + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + ...(tokenStateEntry.GSIPK_clientId_purposeId + ? { + GSIPK_clientId_purposeId: { + S: tokenStateEntry.GSIPK_clientId_purposeId, + }, + } + : {}), + ...(tokenStateEntry.agreementState + ? { + agreementState: { + S: tokenStateEntry.agreementState, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_eserviceId_descriptorId + ? { + GSIPK_eserviceId_descriptorId: { + S: tokenStateEntry.GSIPK_eserviceId_descriptorId, + }, + } + : {}), + ...(tokenStateEntry.GSIPK_purposeId + ? { + GSIPK_purposeId: { + S: tokenStateEntry.GSIPK_purposeId, + }, + } + : {}), + ...(tokenStateEntry.purposeState + ? { + purposeState: { + S: tokenStateEntry.purposeState, + }, + } + : {}), + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const clientKindToTokenGenerationStatesClientKind = ( + kind: ClientKind +): ClientKindTokenStates => + match(kind) + .with(clientKind.consumer, () => clientKindTokenStates.consumer) + .with(clientKind.api, () => clientKindTokenStates.api) + .exhaustive(); + +export const writeClientEntry = async ( + clientEntry: PlatformStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: clientEntry.PK, + }, + state: { + S: clientEntry.state, + }, + clientPurposesIds: { + L: clientEntry.clientPurposesIds.map((purposeId) => ({ + S: purposeId, + })), + }, + clientKind: { + S: clientEntry.clientKind, + }, + clientConsumerId: { + S: clientEntry.clientConsumerId, + }, + version: { + N: clientEntry.version.toString(), + }, + updatedAt: { + S: clientEntry.updatedAt, + }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const readClientEntriesInTokenGenerationStates = async ( + GSIPK_clientId: ClientId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + GSIPK_clientId: ClientId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Client", + KeyConditionExpression: `GSIPK_clientId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: GSIPK_clientId }, + }, + ExclusiveStartKey: exclusiveStartKey, + ScanIndexForward: false, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform state client entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const clientEntries = z + .array(TokenGenerationStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!clientEntries.success) { + throw genericInternalError( + `Unable to parse token state entry items: result ${JSON.stringify( + clientEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return clientEntries.data; + } else { + return [ + ...clientEntries.data, + ...(await runPaginatedQuery( + GSIPK_clientId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); +}; + +export const cleanClientPurposeIdsInPlatformStatesEntry = async ( + primaryKey: PlatformStatesClientPK, + version: number, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":clientPurposesIds": { + L: [], + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET clientPurposesIds = :clientPurposesIds, updatedAt = :newUpdateAt, version = :newVersion", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const extractKidFromTokenEntryPK = ( + pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK +): string => pk.split("#")[2]; + +export const extractAgreementIdFromAgreementPK = ( + pk: PlatformStatesAgreementPK +): AgreementId => { + const substrings = pk.split("#"); + const agreementId = substrings[1]; + const result = AgreementId.safeParse(agreementId); + + if (!result.success) { + throw genericInternalError( + `Unable to parse agreement PK: result ${JSON.stringify( + result + )} - data ${JSON.stringify(agreementId)} ` + ); + } + return result.data; +}; + +export const retrievePlatformStatesByPurpose = async ( + purposeId: PurposeId, + dynamoDBClient: DynamoDBClient +): Promise<{ + purposeEntry?: PlatformStatesPurposeEntry; + agreementEntry?: PlatformStatesAgreementEntry; + catalogEntry?: PlatformStatesCatalogEntry; +}> => { + const purposePK = makePlatformStatesPurposePK(purposeId); + const purposeEntry = await readPlatformPurposeEntry( + purposePK, + dynamoDBClient + ); + + if (!purposeEntry) { + return { + purposeEntry: undefined, + }; + } + + const agreementGSI = makeGSIPKConsumerIdEServiceId({ + eserviceId: purposeEntry.purposeEserviceId, + consumerId: purposeEntry.purposeConsumerId, + }); + + const agreementEntry = + await readPlatformAgreementEntryByGSIPKConsumerIdEServiceId( + agreementGSI, + dynamoDBClient + ); + + if (!agreementEntry) { + return { + purposeEntry, + agreementEntry: undefined, + }; + } + + const catalogPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }); + const catalogEntry = await readCatalogEntry(catalogPK, dynamoDBClient); + + if (!catalogEntry) { + return { + purposeEntry, + agreementEntry, + catalogEntry: undefined, + }; + } + return { + purposeEntry, + agreementEntry, + catalogEntry, + }; +}; + +export const upsertPlatformClientEntry = async ( + entry: PlatformStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + Item: { + PK: { + S: entry.PK, + }, + state: { + S: entry.state, + }, + clientPurposesIds: { + L: entry.clientPurposesIds.map((purposeId) => ({ + S: purposeId, + })), + }, + clientKind: { + S: entry.clientKind, + }, + clientConsumerId: { + S: entry.clientConsumerId, + }, + version: { + N: entry.version.toString(), + }, + updatedAt: { + S: entry.updatedAt, + }, + }, + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const upsertTokenClientKidEntry = async ( + entry: TokenGenerationStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + Item: { + PK: { + S: entry.PK, + }, + consumerId: { + S: entry.consumerId, + }, + clientKind: { + S: entry.clientKind, + }, + publicKey: { + S: entry.publicKey, + }, + GSIPK_clientId: { + S: entry.GSIPK_clientId, + }, + GSIPK_kid: { + S: entry.GSIPK_kid, + }, + updatedAt: { + S: entry.updatedAt, + }, + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const updateTokenDataForSecondRetrieval = async ({ + dynamoDBClient, + entry, + purposeEntry, + agreementEntry, + catalogEntry, +}: { + dynamoDBClient: DynamoDBClient; + entry: TokenGenerationStatesClientPurposeEntry; + purposeEntry?: PlatformStatesPurposeEntry; + agreementEntry?: PlatformStatesAgreementEntry; + catalogEntry?: PlatformStatesCatalogEntry; +}): Promise => { + const setIfChanged = < + K extends keyof TokenGenerationStatesClientPurposeEntry + >( + key: K, + newValue: TokenGenerationStatesClientPurposeEntry[K] + ): Partial => { + const oldValue = entry[key]; + + if (Array.isArray(oldValue) && Array.isArray(newValue)) { + return !oldValue.every((value) => newValue.includes(value)) + ? { [key]: newValue } + : {}; + } + + return oldValue !== newValue ? { [key]: newValue } : {}; + }; + const updatedFields: Partial = { + ...(purposeEntry + ? { + ...setIfChanged( + "GSIPK_consumerId_eserviceId", + makeGSIPKConsumerIdEServiceId({ + consumerId: purposeEntry.purposeConsumerId, + eserviceId: purposeEntry.purposeEserviceId, + }) + ), + ...setIfChanged("purposeVersionId", purposeEntry.purposeVersionId), + ...setIfChanged("purposeState", purposeEntry.state), + } + : {}), + ...(purposeEntry && agreementEntry + ? { + ...setIfChanged( + "GSIPK_eserviceId_descriptorId", + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }) + ), + ...setIfChanged("agreementState", agreementEntry.state), + } + : {}), + ...(catalogEntry + ? { + ...setIfChanged( + "descriptorAudience", + catalogEntry.descriptorAudience + ), + ...setIfChanged( + "descriptorVoucherLifespan", + catalogEntry.descriptorVoucherLifespan + ), + ...setIfChanged("descriptorState", catalogEntry.state), + } + : {}), + }; + + if (Object.keys(updatedFields).length > 0) { + const { expressionAttributeValues, updateExpression } = + generateUpdateItemInputData(updatedFields); + + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: expressionAttributeValues, + UpdateExpression: updateExpression, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; + +const convertValueToAttributeValue = ( + value: string | number | boolean | Array +): AttributeValue => { + if (typeof value === "string") { + return { S: value }; + } else if (typeof value === "number") { + return { N: value.toString() }; + } else if (typeof value === "boolean") { + return { BOOL: value }; + } else if (Array.isArray(value)) { + return { L: value.map((item) => convertValueToAttributeValue(item)) }; + } else { + throw genericInternalError( + `Unsupported DynamoDB type ${typeof value} while converting to AttributeValue` + ); + } +}; + +const convertToExpressionAttributeValues = ( + updatedFields: Partial +): Record => { + const expressionAttributeValues = Object.keys(updatedFields).reduce( + (acc, key) => { + const value = updatedFields[key as keyof typeof updatedFields]; + if (value !== undefined) { + const dynamoKey = `:${key}`; + return { + ...acc, + [dynamoKey]: convertValueToAttributeValue(value), + }; + } + return acc; + }, + {} + ); + + return { + ...expressionAttributeValues, + ":newUpdateAt": { S: new Date().toISOString() }, + }; +}; + +const generateUpdateItemInputData = ( + updatedFields: Partial +): { + updateExpression: string; + expressionAttributeValues: Record; +} => { + const expressionAttributeValues = + convertToExpressionAttributeValues(updatedFields); + + const updateExpressionTmp = Object.keys(updatedFields) + .map((key) => `${key} = :${key}`) + .join(", "); + + const updateExpression = `SET updatedAt = :newUpdateAt, ${updateExpressionTmp}`; + + return { + updateExpression, + expressionAttributeValues, + }; +}; + +export const createTokenClientPurposeEntry = ({ + tokenEntry: baseEntry, + kid, + clientId, + consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, +}: { + tokenEntry: TokenGenerationStatesGenericEntry; + kid: string; + clientId: ClientId; + consumerId: TenantId; + purposeId: PurposeId; + purposeEntry?: PlatformStatesPurposeEntry; + agreementEntry?: PlatformStatesAgreementEntry; + catalogEntry?: PlatformStatesCatalogEntry; +}): TokenGenerationStatesClientPurposeEntry => { + const pk = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + const isTokenClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.safeParse(baseEntry).success; + + return { + PK: pk, + consumerId: baseEntry.consumerId, + updatedAt: new Date().toISOString(), + clientKind: isTokenClientPurposeEntry + ? baseEntry.clientKind + : clientKindTokenStates.consumer, + publicKey: baseEntry.publicKey, + GSIPK_clientId: baseEntry.GSIPK_clientId, + GSIPK_kid: isTokenClientPurposeEntry + ? baseEntry.GSIPK_kid + : makeGSIPKKid(kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...(purposeEntry && { + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + }), + ...(purposeEntry && + agreementEntry && { + agreementId: extractAgreementIdFromAgreementPK(agreementEntry.PK), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + }), + ...(catalogEntry && { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + }), + }; +}; diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts new file mode 100644 index 0000000000..bb8fce9f23 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -0,0 +1,2768 @@ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockClient, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + readAllPlatformStateItems, + readAllTokenStateItems, + getMockPurpose, + getMockPurposeVersion, + writePlatformPurposeEntry, + getMockAgreement, + writePlatformAgreementEntry, + getMockDescriptor, + writeCatalogEntry, + getMockKey, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { + AuthorizationEventEnvelope, + Client, + ClientDeletedV2, + ClientId, + ClientKeyAddedV2, + ClientKeyDeletedV2, + ClientPurposeAddedV2, + ClientPurposeRemovedV2, + Descriptor, + generateId, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeId, + purposeVersionState, + TenantId, + toClientV2, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, +} from "pagopa-interop-models"; +import { handleMessageV2 } from "../src/consumerServiceV2.js"; +import { + clientKindToTokenGenerationStatesClientKind, + readClientEntry, + writeClientEntry, + writeTokenStateClientEntry, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("integration tests V2 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("ClientKeyAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [key], + purposes: [generateId()], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: key.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should insert platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and insert token-generation-states client-kid-purpose entries", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid-purpose entries", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const tokenClientKidPurposePK3 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK4 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId3 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId4 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK3), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK4), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should insert platform-states entry and insert token-generation-states client-kid entry if the client does not contain purposes", async () => { + const messageVersion = 1; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: messageVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + ); + }); + + it("should update platform-states entry and insert token-generation-states client-kid entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: ClientKeyAddedV2 = { + client: toClientV2(client), + kid: addedKey.kid, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }); + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry1, expectedTokenClientEntry]) + ); + }); + }); + + describe("ClientKeyDeleted", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: ClientKeyDeletedV2 = { + client: toClientV2(client), + kid: kidToRemove, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kidToRemove, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kidToRemove), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should insert platform-states entry and delete token-generation-states entries for that kid", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: ClientKeyDeletedV2 = { + client: toClientV2(client), + kid: kidToRemove, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + const tokenClientPurposeEntryWithOtherKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId: client.id, + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + clientPurposesIds: [], + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([ + tokenClientPurposeEntryWithOtherKid, + ]); + }); + + it("should update platform-states entry and delete token-generation-states entries for that kid", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + const otherKid = "other kid"; + + const payload: ClientKeyDeletedV2 = { + client: toClientV2(client), + kid: kidToRemove, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientKeyDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: otherKid, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + + const tokenClientEntryWithOtherKid: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(otherKid), + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntryWithOtherKid]); + }); + }); + + describe("ClientPurposeAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update only platform-states entry if there are no keys in the client", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientPurposeEntry = getMockTokenStatesClientPurposeEntry(); + const tokenClientEntry = getMockTokenStatesClientEntry(); + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientPurposeEntry, tokenClientEntry]) + ); + }); + + it("should update platform-states entry and convert client-kid entries to client-kid-purpose entries in token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose.id], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId: purpose.id, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry, dynamoDBClient); + + const agreement = getMockAgreement(); + const platformAgreementEntry: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + await writePlatformAgreementEntry(platformAgreementEntry, dynamoDBClient); + + const descriptor: Descriptor = { + ...getMockDescriptor(), + id: agreement.descriptorId, + }; + const previousDescriptorEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid1, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid2, + }); + + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + }; + const tokenClientPurposeEntryWithOtherClient = + getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: platformPurposeEntry.state, + purposeVersionId: platformPurposeEntry.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry.GSIPK_consumerId_eserviceId, + agreementId: agreement.id, + agreementState: platformAgreementEntry.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + descriptorState: previousDescriptorEntry.state, + descriptorAudience: previousDescriptorEntry.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(3); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntryWithOtherClient, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and insert client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId: purpose2.id, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV2 = { + purposeId: purpose2.id, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + const tokenClientKidPK3 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose2.id, + }); + const tokenClientKidPK4 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK3), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK4), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + }); + + describe("ClientPurposeRemoved", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the purpose platform-states entry doesn't exist and the token-generation-states entries aren't associated to the purpose id in the message", async () => { + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId1 = generateId(); + const purposeId2 = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId1], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId: purposeId2, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId1, purposeId2], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const mockClientKidPurpose1 = "mockClientKidPurpose1"; + const mockClientKidPurpose2 = "mockClientKidPurpose2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose1, + purposeId: purposeId1, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose2, + purposeId: purposeId2, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId1, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId2, + }); + + const tokenClientEntry = getMockTokenStatesClientEntry(); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_purposeId: purposeId1, + }; + + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_purposeId: purposeId2, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, tokenClientPurposeEntry1]) + ); + }); + }); + + describe("ClientDeleted", () => { + it("should delete platform-states entry and token-generation-states entries", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client = getMockClient(); + + const payload: ClientDeletedV2 = { + client: toClientV2(client), + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientDeleted", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + const otherClientId = generateId(); + + // platform-states + const pk1PlatformStates = makePlatformStatesClientPK(client.id); + const clientPlatformStateEntry1: PlatformStatesClientEntry = { + PK: pk1PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + + const pk2PlatformStates = makePlatformStatesClientPK(otherClientId); + const clientPlatformStateEntry2: PlatformStatesClientEntry = { + PK: pk2PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(clientPlatformStateEntry1, dynamoDBClient); + await writeClientEntry(clientPlatformStateEntry2, dynamoDBClient); + + // token-generation-states + const pkTokenStates1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: "kid", + purposeId, + }); + + const pkTokenStates2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: otherClientId, + kid: "kid", + purposeId, + }); + + const clientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates1), + GSIPK_clientId: client.id, + }; + + const otherClientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates2), + GSIPK_clientId: otherClientId, + }; + + await writeTokenStateEntry(clientPurposeTokenStateEntry, dynamoDBClient); + await writeTokenStateEntry( + otherClientPurposeTokenStateEntry, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntries = await readAllPlatformStateItems( + dynamoDBClient + ); + expect(retrievedPlatformStatesEntries).toEqual([ + clientPlatformStateEntry2, + ]); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([ + otherClientPurposeTokenStateEntry, + ]); + }); + }); +}); diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts new file mode 100644 index 0000000000..7d3e7b2b48 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -0,0 +1,779 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { fail } from "assert"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockAgreementEntry, + getMockKey, + getMockPlatformStatesClientEntry, + getMockPurposeVersion, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + getMockPurpose, + getMockClient, + readAllPlatformStateItems, + readAllTokenStateItems, + writeCatalogEntry, + writePlatformAgreementEntry, + writePlatformPurposeEntry, + writeTokenStateEntry, + getMockAgreement, + getMockDescriptor, + getMockEService, +} from "pagopa-interop-commons-test"; +import { + Agreement, + AgreementId, + Client, + ClientId, + clientKindTokenStates, + DescriptorId, + EService, + EServiceId, + generateId, + GSIPKKid, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeId, + purposeVersionState, + TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericEntry, + unsafeBrandId, +} from "pagopa-interop-models"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { z } from "zod"; +import { + cleanClientPurposeIdsInPlatformStatesEntry, + convertEntriesToClientKidInTokenGenerationStates, + deleteClientEntryFromPlatformStates, + deleteClientEntryFromTokenGenerationStatesTable, + deleteEntriesFromTokenStatesByClient, + deleteEntriesFromTokenStatesByKid, + deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + readClientEntriesInTokenGenerationStates, + readClientEntry, + readPlatformAgreementEntryByGSIPKConsumerIdEServiceId, + retrievePlatformStatesByPurpose, + updateTokenDataForSecondRetrieval, + upsertPlatformClientEntry, + upsertTokenClientKidEntry, + upsertTokenStateClientPurposeEntry, + writeClientEntry, + writeTokenStateClientEntry, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("utils", () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + it("deleteEntriesFromTokenStatesByKid", async () => { + const kid = unsafeBrandId("mock kid"); + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + GSIPK_kid: kid, + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_kid: kid, + }; + + const otherClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + await writeTokenStateEntry(otherClientPurposeEntry, dynamoDBClient); + + await deleteEntriesFromTokenStatesByKid(kid, dynamoDBClient); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([otherClientPurposeEntry]); + }); + + it("deleteClientEntryFromPlatformStates", async () => { + const pk1 = makePlatformStatesClientPK(generateId()); + const pk2 = makePlatformStatesClientPK(generateId()); + + const clientEntry1: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(pk1), + }; + const clientEntry2: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(pk2), + }; + + await writeClientEntry(clientEntry1, dynamoDBClient); + await writeClientEntry(clientEntry2, dynamoDBClient); + + await deleteClientEntryFromPlatformStates(pk1, dynamoDBClient); + + const res = await readAllPlatformStateItems(dynamoDBClient); + + expect(res).toEqual([clientEntry2]); + }); + + it("deleteEntriesFromTokenStatesByClient", async () => { + const GSIPK_clientId = generateId(); + + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + GSIPK_clientId, + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId, + }; + + const otherClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + await writeTokenStateEntry(otherClientPurposeEntry, dynamoDBClient); + + await deleteEntriesFromTokenStatesByClient(GSIPK_clientId, dynamoDBClient); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([otherClientPurposeEntry]); + }); + + describe("deleteClientEntryFromTokenGenerationStatesTable", () => { + it("clientKid entry", async () => { + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + + await deleteClientEntryFromTokenGenerationStatesTable( + clientEntry, + dynamoDBClient + ); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientPurposeEntry]); + }); + it("clientKidPurpose entry", async () => { + const clientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + }; + + const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + + await deleteClientEntryFromTokenGenerationStatesTable( + clientPurposeEntry, + dynamoDBClient + ); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientEntry]); + }); + }); + + it("readClientEntry", async () => { + const clientEntry1 = getMockPlatformStatesClientEntry(); + + const clientEntry2 = getMockPlatformStatesClientEntry(); + + await writeClientEntry(clientEntry1, dynamoDBClient); + await writeClientEntry(clientEntry2, dynamoDBClient); + + const res = await readClientEntry(clientEntry1.PK, dynamoDBClient); + + expect(res).toEqual(clientEntry1); + }); + + it("deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable", async () => { + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId: generateId(), + purposeId: generateId(), + }); + const clientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId_purposeId, + }; + + const clientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId_purposeId, + }; + + const clientPurposeEntry3 = getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateEntry(clientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(clientPurposeEntry3, dynamoDBClient); + + await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientPurposeEntry3]); + }); + + it("convertEntriesToClientKidInTokenGenerationStates", async () => { + const clientId = generateId(); + const kid1 = "kid1"; + const kid2 = "kid2"; + + const purposeId = generateId(); + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }); + + const pk1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: kid1, + purposeId, + }); + const clientKidPurposeEntry1: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(pk1), + GSIPK_clientId_purposeId, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid1), + }; + + const pk2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: kid2, + purposeId, + }); + const clientKidPurposeEntry2: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(pk2), + GSIPK_clientId_purposeId, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid2), + }; + + const clientKidPurposeEntry3 = getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateEntry(clientKidPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(clientKidPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(clientKidPurposeEntry3, dynamoDBClient); + + await convertEntriesToClientKidInTokenGenerationStates( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + + const expectedEntry1: TokenGenerationStatesClientEntry = { + consumerId: clientKidPurposeEntry1.consumerId, + updatedAt: new Date().toISOString(), + PK: makeTokenGenerationStatesClientKidPK({ clientId, kid: kid1 }), + clientKind: clientKindTokenStates.consumer, + publicKey: clientKidPurposeEntry1.publicKey, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid1), + }; + + const expectedEntry2: TokenGenerationStatesClientEntry = { + consumerId: clientKidPurposeEntry2.consumerId, + updatedAt: new Date().toISOString(), + PK: makeTokenGenerationStatesClientKidPK({ clientId, kid: kid2 }), + clientKind: "CONSUMER", + publicKey: clientKidPurposeEntry1.publicKey, + GSIPK_clientId: clientId, + GSIPK_kid: unsafeBrandId(kid2), + }; + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual( + expect.arrayContaining([ + expectedEntry2, + expectedEntry1, + clientKidPurposeEntry3, + ]) + ); + }); + + describe("writeTokenStateClientEntry", () => { + it("should succeed if the entry doesn't exist", async () => { + const clientEntry = getMockTokenStatesClientEntry(); + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + + const result = await readAllTokenStateItems(dynamoDBClient); + expect(result).toEqual([clientEntry]); + }); + + it("should throw error if the entry already exists", async () => { + const clientEntry = getMockTokenStatesClientEntry(); + await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + + expect( + writeTokenStateClientEntry(clientEntry, dynamoDBClient) + ).rejects.toThrowError(); + }); + }); + + it("readPlatformAgreementEntryByGSIPKConsumerIdEServiceId", async () => { + const pk1 = makePlatformStatesAgreementPK(generateId()); + const pk2 = makePlatformStatesAgreementPK(generateId()); + const pk3 = makePlatformStatesAgreementPK(generateId()); + + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }); + + const threeHoursAgo = new Date(); + threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); + + const agreementEntry1: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(pk1, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: threeHoursAgo.toISOString(), + }; + + const agreementEntry2: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(pk2, GSIPK_consumerId_eserviceId), + GSISK_agreementTimestamp: new Date().toISOString(), + }; + + const agreementEntry3 = getMockAgreementEntry(pk3); + + await writePlatformAgreementEntry(agreementEntry1, dynamoDBClient); + await writePlatformAgreementEntry(agreementEntry2, dynamoDBClient); + await writePlatformAgreementEntry(agreementEntry3, dynamoDBClient); + + const res = await readPlatformAgreementEntryByGSIPKConsumerIdEServiceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); + + expect(res).toEqual(agreementEntry2); + }); + + describe("upsertTokenStateClientPurposeEntry", () => { + it("write", async () => { + const clientKidPurposeEntry = getMockTokenStatesClientPurposeEntry(); + + const resultBefore = await readAllTokenStateItems(dynamoDBClient); + expect(resultBefore).toEqual([]); + + await upsertTokenStateClientPurposeEntry( + clientKidPurposeEntry, + dynamoDBClient + ); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([clientKidPurposeEntry]); + }); + it("update", async () => { + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + descriptorState: itemState.active, + }; + await writeTokenStateEntry(clientKidPurposeEntry, dynamoDBClient); + + const updatedEntry: TokenGenerationStatesClientPurposeEntry = { + ...clientKidPurposeEntry, + descriptorState: itemState.inactive, + }; + await upsertTokenStateClientPurposeEntry(updatedEntry, dynamoDBClient); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([updatedEntry]); + }); + }); + + it("readClientEntriesInTokenGenerationStates", async () => { + const clientId = generateId(); + const pk1 = makeTokenGenerationStatesClientKidPK({ clientId, kid: "" }); + const pk2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: "", + purposeId: generateId(), + }); + + const GSIPK_clientId = clientId; + + const clientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(pk1), + GSIPK_clientId, + }; + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(pk2), + GSIPK_clientId, + }; + + await writeTokenStateClientEntry(clientKidEntry, dynamoDBClient); + await writeTokenStateEntry(clientKidPurposeEntry, dynamoDBClient); + + const res = await readClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + + expect(res).toEqual( + expect.arrayContaining([clientKidEntry, clientKidPurposeEntry]) + ); + }); + + it("cleanClientPurposeIdsInPlatformStatesEntry", async () => { + const clientEntry1: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientPurposesIds: [generateId(), generateId()], + }; + const clientEntry2: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientPurposesIds: [generateId(), generateId()], + }; + + await writeClientEntry(clientEntry1, dynamoDBClient); + await writeClientEntry(clientEntry2, dynamoDBClient); + + await cleanClientPurposeIdsInPlatformStatesEntry( + clientEntry1.PK, + clientEntry1.version + 1, + dynamoDBClient + ); + + const res = await readAllPlatformStateItems(dynamoDBClient); + expect(res).toEqual( + expect.arrayContaining([ + { + ...clientEntry1, + clientPurposesIds: [], + version: clientEntry1.version + 1, + }, + clientEntry2, + ]) + ); + }); + + it("retrievePlatformStatesByPurpose", async () => { + const purposeId = generateId(); + const purposePK = makePlatformStatesPurposePK(purposeId); + const agreementId = generateId(); + const eserviceId = generateId(); + const descriptorId = generateId(); + const consumerId = generateId(); + + const purposeEntry: PlatformStatesPurposeEntry = { + PK: purposePK, + state: itemState.inactive, + purposeVersionId: generateId(), + purposeEserviceId: eserviceId, + purposeConsumerId: consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writePlatformPurposeEntry(purposeEntry, dynamoDBClient); + + const agreementPK = makePlatformStatesAgreementPK(agreementId); + const agreementEntry: PlatformStatesAgreementEntry = { + ...getMockAgreementEntry(agreementPK), + agreementDescriptorId: descriptorId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }), + }; + + await writePlatformAgreementEntry(agreementEntry, dynamoDBClient); + + const catalogPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId, + descriptorId, + }); + const catalogEntry: PlatformStatesCatalogEntry = { + PK: catalogPK, + state: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + version: 3, + updatedAt: new Date().toISOString(), + }; + + await writeCatalogEntry(catalogEntry, dynamoDBClient); + + const res = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + expect(res).toEqual({ + purposeEntry, + agreementEntry, + catalogEntry, + }); + }); + + describe("upsertPlatformClientEntry", () => { + it("write", async () => { + const clientEntry: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + + const resultBefore = await readAllPlatformStateItems(dynamoDBClient); + expect(resultBefore).toEqual([]); + + await upsertPlatformClientEntry(clientEntry, dynamoDBClient); + + const resultAfter = await readAllPlatformStateItems(dynamoDBClient); + expect(resultAfter).toEqual([clientEntry]); + }); + it("update", async () => { + const clientEntry: PlatformStatesClientEntry = { + ...getMockPlatformStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + + await writeClientEntry(clientEntry, dynamoDBClient); + + const updatedEntry: PlatformStatesClientEntry = { + ...clientEntry, + clientKind: clientKindTokenStates.api, + }; + await upsertPlatformClientEntry(updatedEntry, dynamoDBClient); + + const resultAfter = await readAllPlatformStateItems(dynamoDBClient); + expect(resultAfter).toEqual([updatedEntry]); + }); + }); + + describe("upsertTokenClientKidEntry", () => { + it("write", async () => { + const clientKidEntry = getMockTokenStatesClientEntry(); + + const resultBefore = await readAllTokenStateItems(dynamoDBClient); + expect(resultBefore).toEqual([]); + + await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([clientKidEntry]); + }); + it("update", async () => { + const clientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + await writeTokenStateClientEntry(clientKidEntry, dynamoDBClient); + + const updatedEntry: TokenGenerationStatesClientEntry = { + ...clientKidEntry, + clientKind: clientKindTokenStates.api, + }; + await upsertTokenClientKidEntry(updatedEntry, dynamoDBClient); + + const resultAfter = await readAllTokenStateItems(dynamoDBClient); + expect(resultAfter).toEqual([updatedEntry]); + }); + }); + + it("parsing TokenGenerationStatesGenericEntry", () => { + const clientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + }; + const entries1 = z + .array(TokenGenerationStatesGenericEntry) + .safeParse([clientKidEntry]); + + expect(entries1.data![0]).toEqual(clientKidEntry); + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), + }; + const entries2 = z + .array(TokenGenerationStatesGenericEntry) + .safeParse([clientKidPurposeEntry]); + + expect(entries2.data![0]).toEqual(clientKidPurposeEntry); + + const clientId = generateId(); + const clientKidPurposeEntryWithUndefined: TokenGenerationStatesClientPurposeEntry = + { + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: "kid", + purposeId: generateId(), + }), + consumerId: generateId(), + clientKind: clientKindTokenStates.api, + publicKey: "publicKey", + GSIPK_clientId: generateId(), + GSIPK_kid: unsafeBrandId("kid"), + updatedAt: new Date().toISOString(), + }; + const entries3 = z + .array(TokenGenerationStatesGenericEntry) + .safeParse([clientKidPurposeEntryWithUndefined]); + + expect(entries3.data![0]).toEqual(clientKidPurposeEntryWithUndefined); + }); + + it("updateTokenDataForSecondRetrieval", async () => { + const consumerId = generateId(); + const descriptor = getMockDescriptor(); + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const purpose: Purpose = { + ...getMockPurpose(), + consumerId, + eserviceId: eservice.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [key], + purposes: [purpose.id], + }; + const agreement: Agreement = { + ...getMockAgreement(), + eserviceId: eservice.id, + descriptorId: descriptor.id, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId: client.id, + kid: key.kid, + purposeId: purpose.id, + } + ); + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeVersionId: purpose.versions[0].id, + descriptorAudience: descriptor.audience, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: eservice.id, + }), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }), + }; + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + + const platformPurposeEntry: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose.id), + version: 1, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: eservice.id, + purposeConsumerId: consumerId, + }; + + const platformAgreementEntry: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement.id), + version: 1, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: eservice.id, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + const platformCatalogEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + state: itemState.inactive, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry: tokenClientPurposeEntry, + purposeEntry: platformPurposeEntry, + agreementEntry: platformAgreementEntry, + catalogEntry: platformCatalogEntry, + }); + + const retrievedTokenEntries = await readAllTokenStateItems(dynamoDBClient); + const expectedTokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry, + purposeState: itemState.inactive, + agreementState: itemState.inactive, + descriptorState: itemState.inactive, + }; + expect(retrievedTokenEntries).toHaveLength(1); + expect(retrievedTokenEntries[0]).toEqual(expectedTokenClientPurposeEntry); + }); +}); diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 15a439945e..ff2f045b04 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -51,6 +51,13 @@ import { GSIPKConsumerIdEServiceId, PlatformStatesAgreementEntry, PlatformStatesAgreementPK, + makeGSIPKKid, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientEntry, + makeTokenGenerationStatesClientKidPK, + PlatformStatesClientPK, + PlatformStatesClientEntry, + makePlatformStatesClientPK, } from "pagopa-interop-models"; import { AuthData } from "pagopa-interop-commons"; import { z } from "zod"; @@ -334,13 +341,14 @@ export const getMockTokenStatesClientPurposeEntry = ( const descriptorId = generateId(); const agreementId = generateId(); const purposeVersionId = generateId(); + const kid = `kid ${Math.random()}`; return { PK: tokenStateEntryPK || makeTokenGenerationStatesClientKidPurposePK({ clientId, - kid: `kid ${Math.random()}`, + kid, purposeId, }), descriptorState: itemState.inactive, @@ -357,8 +365,8 @@ export const getMockTokenStatesClientPurposeEntry = ( clientKind: clientKindTokenStates.consumer, publicKey: "PEM", GSIPK_clientId: clientId, - GSIPK_kid: "KID", - agreementState: "ACTIVE", + GSIPK_kid: makeGSIPKKid(kid), + agreementState: itemState.active, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ eserviceId, descriptorId, @@ -389,3 +397,38 @@ export const getMockAgreementEntry = ( GSISK_agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }); + +export const getMockTokenStatesClientEntry = ( + tokenStateEntryPK?: TokenGenerationStatesClientKidPK +): TokenGenerationStatesClientEntry => { + const clientId = generateId(); + const consumerId = generateId(); + const kid = `kid ${Math.random()}`; + + return { + PK: + tokenStateEntryPK || + makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }), + updatedAt: new Date().toISOString(), + consumerId, + clientKind: clientKindTokenStates.consumer, + publicKey: "PEM", + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + }; +}; + +export const getMockPlatformStatesClientEntry = ( + pk?: PlatformStatesClientPK +): PlatformStatesClientEntry => ({ + PK: pk || makePlatformStatesClientPK(generateId()), + version: 0, + state: "ACTIVE", + updatedAt: new Date().toISOString(), + clientKind: "CONSUMER", + clientConsumerId: generateId(), + clientPurposesIds: [], +}); diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 36a78b5c87..18f5e7df98 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { AttributeValue, DynamoDBClient, @@ -17,7 +15,11 @@ import { GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, PlatformStatesCatalogEntry, + PlatformStatesGenericEntry, TokenGenerationStatesClientPurposeEntry, + PlatformStatesPurposeEntry, + PlatformStatesAgreementEntry, + TokenGenerationStatesGenericEntry, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; @@ -138,7 +140,7 @@ export const writeTokenStateEntry = async ( export const readAllTokenStateItems = async ( dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const readInput: ScanInput = { TableName: "token-generation-states", }; @@ -153,7 +155,7 @@ export const readAllTokenStateItems = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + .array(TokenGenerationStatesGenericEntry) .safeParse(unmarshalledItems); if (!tokenStateEntries.success) { @@ -167,6 +169,37 @@ export const readAllTokenStateItems = async ( } }; +export const readAllPlatformStateItems = async ( + dynamoDBClient: DynamoDBClient +): Promise => { + const readInput: ScanInput = { + TableName: "platform-states", + }; + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const platformStateEntries = z + .array(PlatformStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!platformStateEntries.success) { + throw genericInternalError( + `Unable to parse platform state entry item: result ${JSON.stringify( + platformStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + return platformStateEntries.data; + } +}; + export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient @@ -324,3 +357,73 @@ export const writeCatalogEntry = async ( const command = new PutItemCommand(input); await dynamoDBClient.send(command); }; + +export const writePlatformPurposeEntry = async ( + purposeEntry: PlatformStatesPurposeEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: purposeEntry.PK, + }, + state: { + S: purposeEntry.state, + }, + purposeVersionId: { + S: purposeEntry.purposeVersionId, + }, + purposeEserviceId: { + S: purposeEntry.purposeEserviceId, + }, + purposeConsumerId: { + S: purposeEntry.purposeConsumerId, + }, + version: { + N: purposeEntry.version.toString(), + }, + updatedAt: { + S: purposeEntry.updatedAt, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const writePlatformAgreementEntry = async ( + agreementEntry: PlatformStatesAgreementEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: agreementEntry.PK, + }, + state: { + S: agreementEntry.state, + }, + version: { + N: agreementEntry.version.toString(), + }, + updatedAt: { + S: agreementEntry.updatedAt, + }, + GSIPK_consumerId_eserviceId: { + S: agreementEntry.GSIPK_consumerId_eserviceId, + }, + GSISK_agreementTimestamp: { + S: agreementEntry.GSISK_agreementTimestamp, + }, + agreementDescriptorId: { + S: agreementEntry.agreementDescriptorId, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index d2e9470fb0..84d0dd1708 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -101,16 +101,20 @@ export type GSIPKConsumerIdEServiceId = z.infer< typeof GSIPKConsumerIdEServiceId >; +export const clientKidPurposePrefix = "CLIENTKIDPURPOSE#"; export const TokenGenerationStatesClientKidPurposePK = z .string() - .brand(`CLIENTKIDPURPOSE#clientId#kid#purposeId`); + .refine((pk) => pk.startsWith(clientKidPurposePrefix)) + .brand(`${clientKidPurposePrefix}clientId#kid#purposeId`); export type TokenGenerationStatesClientKidPurposePK = z.infer< typeof TokenGenerationStatesClientKidPurposePK >; +export const clientKidPrefix = "CLIENTKID#"; export const TokenGenerationStatesClientKidPK = z .string() - .brand(`CLIENTKID#clientId#kid`); + .refine((pk) => pk.startsWith(clientKidPrefix)) + .brand(`${clientKidPrefix}clientId#kid`); export type TokenGenerationStatesClientKidPK = z.infer< typeof TokenGenerationStatesClientKidPK >; @@ -125,6 +129,9 @@ export type GSIPKEServiceIdDescriptorId = z.infer< export const GSIPKClientIdPurposeId = z.string().brand(`clientId#purposeId`); export type GSIPKClientIdPurposeId = z.infer; +export const GSIPKKid = z.string().brand("kid"); +export type GSIPKKid = z.infer; + type IDS = | CorrelationId | EServiceId @@ -153,7 +160,8 @@ type IDS = | TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK | GSIPKEServiceIdDescriptorId - | GSIPKClientIdPurposeId; + | GSIPKClientIdPurposeId + | GSIPKKid; // 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/token-generation-readmodel/commons.ts b/packages/models/src/token-generation-readmodel/commons.ts index 86308beab5..ea1cc7ea38 100644 --- a/packages/models/src/token-generation-readmodel/commons.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -16,6 +16,7 @@ import { GSIPKEServiceIdDescriptorId, GSIPKClientIdPurposeId, unsafeBrandId, + GSIPKKid, } from "../brandedIds.js"; export const makePlatformStatesEServiceDescriptorPK = ({ @@ -95,6 +96,9 @@ export const makeGSIPKClientIdPurposeId = ({ }): GSIPKClientIdPurposeId => unsafeBrandId(`${clientId}#${purposeId}`); +export const makeGSIPKKid = (kid: string): GSIPKKid => + unsafeBrandId(kid); + export const clientKindTokenStates = { consumer: "CONSUMER", api: "API", diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index e12c8ae6ec..4563e3b8db 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -68,3 +68,12 @@ export const PlatformStatesClientEntry = PlatformStatesBaseEntry.extend({ export type PlatformStatesClientEntry = z.infer< typeof PlatformStatesClientEntry >; + +export const PlatformStatesGenericEntry = PlatformStatesCatalogEntry.or( + PlatformStatesAgreementEntry +) + .or(PlatformStatesPurposeEntry) + .or(PlatformStatesClientEntry); +export type PlatformStatesGenericEntry = z.infer< + typeof PlatformStatesGenericEntry +>; diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index db65a91e19..c36a56cf9a 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -5,6 +5,7 @@ import { GSIPKClientIdPurposeId, GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, + GSIPKKid, PurposeId, PurposeVersionId, TenantId, @@ -19,7 +20,7 @@ const TokenGenerationStatesBaseEntry = z.object({ clientKind: ClientKindTokenStates, publicKey: z.string(), GSIPK_clientId: ClientId, - GSIPK_kid: z.string(), + GSIPK_kid: GSIPKKid, updatedAt: z.string().datetime(), }); type TokenGenerationStatesBaseEntry = z.infer< @@ -50,5 +51,11 @@ export const TokenGenerationStatesClientEntry = PK: TokenGenerationStatesClientKidPK, }); export type TokenGenerationStatesClientEntry = z.infer< - typeof TokenGenerationStatesClientPurposeEntry + typeof TokenGenerationStatesClientEntry +>; + +export const TokenGenerationStatesGenericEntry = + TokenGenerationStatesClientPurposeEntry.or(TokenGenerationStatesClientEntry); +export type TokenGenerationStatesGenericEntry = z.infer< + typeof TokenGenerationStatesGenericEntry >; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9841b24c7..daedbfc6b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -716,9 +716,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -734,9 +731,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -4710,9 +4704,6 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -7440,10 +7431,6 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -11542,8 +11529,6 @@ snapshots: '@types/triple-beam@1.3.5': {} - '@types/uuid@9.0.8': {} - '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -14666,8 +14651,6 @@ snapshots: utils-merge@1.0.1: {} - uuid@10.0.0: {} - uuid@9.0.1: {} vary@1.1.2: {} From d0b6b0e4bc30020ee5b45cbe023c1565b4d60f87 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Thu, 7 Nov 2024 08:51:54 +0100 Subject: [PATCH 016/126] PIN-5566 - fixing dtd catalog exporter docker run (#1170) --- packages/dtd-catalog-exporter/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 48dac47f26..4b8cbd21e8 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "private": true, "description": "PagoPA Interoperability catalog exporter job", + "main": "dist", "type": "module", "scripts": { "test": "vitest", From 53c4fedb3fd00d767ff8109c2ee3b3f0ef0b1ffe Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:16:02 +0100 Subject: [PATCH 017/126] PIN 5615 - Fix wrong anac file download path (#1172) --- .../src/service/sftpService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/anac-certified-attributes-importer/src/service/sftpService.ts b/packages/anac-certified-attributes-importer/src/service/sftpService.ts index db998bf45f..48a1a1f384 100644 --- a/packages/anac-certified-attributes-importer/src/service/sftpService.ts +++ b/packages/anac-certified-attributes-importer/src/service/sftpService.ts @@ -29,7 +29,9 @@ export class SftpClient { logger.info(`Loading file ${fileName}`); - const file = await sftpClient.get(this.config.folderPath + fileName); + const file = await sftpClient.get( + [this.config.folderPath, fileName].join("/") + ); await sftpClient.end(); From ba191ef4f4c16b6743cc0bf3eb22a50cae15e1b5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 7 Nov 2024 15:35:06 +0100 Subject: [PATCH 018/126] PIN-5626 Fix logic for updated descriptor quotas (#1174) --- packages/authorization-updater/src/index.ts | 13 +++- .../test/authorizationUpdater.test.ts | 66 +++++++++++++++++-- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/packages/authorization-updater/src/index.ts b/packages/authorization-updater/src/index.ts index 2ba671348e..0c88769cc6 100644 --- a/packages/authorization-updater/src/index.ts +++ b/packages/authorization-updater/src/index.ts @@ -103,6 +103,18 @@ export async function sendCatalogAuthUpdate( ); } ) + .with({ type: "EServiceDescriptorQuotasUpdated" }, async (msg) => { + const data = getDescriptorFromEvent(msg, decodedMessage.type); + await authService.updateEServiceState( + descriptorStateToClientState(data.descriptor.state), + data.descriptor.id, + data.eserviceId, + data.descriptor.audience, + data.descriptor.voucherLifespan, + logger, + correlationId + ); + }) .with( { type: P.union( @@ -122,7 +134,6 @@ export async function sendCatalogAuthUpdate( "EServiceRiskAnalysisAdded", "EServiceRiskAnalysisUpdated", "EServiceRiskAnalysisDeleted", - "EServiceDescriptorQuotasUpdated", "EServiceDescriptionUpdated" ), }, diff --git a/packages/authorization-updater/test/authorizationUpdater.test.ts b/packages/authorization-updater/test/authorizationUpdater.test.ts index d5a685494c..ab0c7a1d69 100644 --- a/packages/authorization-updater/test/authorizationUpdater.test.ts +++ b/packages/authorization-updater/test/authorizationUpdater.test.ts @@ -136,7 +136,7 @@ describe("Authorization Updater processMessage", () => { "EServiceDescriptorSuspended", "EServiceDescriptorActivated", ])( - "should correctly process a catalog message with type %t and call updateEServiceState", + "should correctly process a catalog message with type %s and call updateEServiceState", async (eventType) => { const descriptor: Descriptor = { ...getMockDescriptorPublished(), @@ -203,6 +203,62 @@ describe("Authorization Updater processMessage", () => { } ); + it("should correctly process a catalog message with type EServiceDescriptorQuotasUpdated and call updateEServiceState", async () => { + const descriptor: Descriptor = { + ...getMockDescriptorPublished(), + version: "1", + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const message: EServiceEventEnvelopeV2 = { + sequence_num: 1, + stream_id: eservice.id, + version: 1, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: { + eservice: toEServiceV2(eservice), + descriptorId: descriptor.id, + }, + log_date: new Date(), + } as EServiceEventEnvelopeV2; + + await sendCatalogAuthUpdate( + message, + authorizationService, + genericLogger, + testCorrelationId + ); + + const expectedUpdateEServiceStatePayload = { + state: authorizationManagementApi.ClientComponentState.Values.ACTIVE, + descriptorId: descriptor.id, + audience: descriptor.audience, + voucherLifespan: descriptor.voucherLifespan, + }; + + expect( + authorizationManagementClients.purposeApiClient.updateEServiceState + ).toHaveBeenCalledTimes(1); + expect( + authorizationManagementClients.purposeApiClient.updateEServiceState + ).toHaveBeenCalledWith(expectedUpdateEServiceStatePayload, { + params: { eserviceId: eservice.id }, + withCredentials: true, + headers: testHeaders, + }); + + expect( + authorizationManagementClients.purposeApiClient.updateAgreementState + ).not.toHaveBeenCalled(); + expect( + authorizationManagementClients.purposeApiClient + .updateAgreementAndEServiceStates + ).not.toHaveBeenCalled(); + }); + it.each([ "AgreementSubmitted", "AgreementActivated", @@ -214,7 +270,7 @@ describe("Authorization Updater processMessage", () => { "AgreementUnsuspendedByProducer", "AgreementArchivedByConsumer", ])( - "Should correctly process an agreement messages with type %t and call updateAgreementState", + "Should correctly process an agreement messages with type %s and call updateAgreementState", async (eventType) => { const agreement: Agreement = getMockAgreement(); @@ -433,7 +489,7 @@ describe("Authorization Updater processMessage", () => { }); it.each(["DraftPurposeDeleted", "WaitingForApprovalPurposeDeleted"])( - "should correctly process purposes message with type %t and call deletePurposeFromClient", + "should correctly process purposes message with type %s and call deletePurposeFromClient", async (eventType) => { const purpose: Purpose = { ...getMockPurpose(), @@ -512,7 +568,7 @@ describe("Authorization Updater processMessage", () => { "PurposeVersionActivated", "PurposeArchived", ])( - "should correctly process purposes message with type %t and call updatePurposeState with ACTIVE state", + "should correctly process purposes message with type %s and call updatePurposeState with ACTIVE state", async (eventType) => { const purposeVersion: PurposeVersion = { ...getMockPurposeVersion(), @@ -575,7 +631,7 @@ describe("Authorization Updater processMessage", () => { "PurposeVersionActivated", "PurposeArchived", ])( - "should correctly process purposes message with type %t and call updatePurposeState with INACTIVE state", + "should correctly process purposes message with type %s and call updatePurposeState with INACTIVE state", async (eventType) => { const purposeVersion: PurposeVersion = { ...getMockPurposeVersion(), From 217c119a6be36900aa5a52c5763b430a6413bfbc Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Thu, 7 Nov 2024 16:32:25 +0100 Subject: [PATCH 019/126] PIN-5627 - small fixes in interop jobs (#1176) --- .../anac-certified-attributes-importer/.env | 16 ++++++------- .../aws.config.local | 4 ---- .../src/index.ts | 5 ++++ .../src/model/tenant.ts | 11 +++++++++ .../src/service/processor.ts | 20 ++++++++++------ .../src/service/readmodelQueriesService.ts | 23 +++++++++++-------- packages/dtd-catalog-exporter/package.json | 2 +- .../ivass-certified-attributes-importer/.env | 11 +++++---- .../src/index.ts | 5 ++++ .../src/model/tenant.ts | 11 +++++++++ .../src/service/processor.ts | 20 ++++++++-------- .../src/service/readModelQueriesService.ts | 17 ++++++++------ packages/one-trust-notices/.env | 4 ++-- packages/one-trust-notices/aws.config.local | 4 ++++ 14 files changed, 101 insertions(+), 52 deletions(-) delete mode 100644 packages/anac-certified-attributes-importer/aws.config.local create mode 100644 packages/anac-certified-attributes-importer/src/model/tenant.ts create mode 100644 packages/ivass-certified-attributes-importer/src/model/tenant.ts diff --git a/packages/anac-certified-attributes-importer/.env b/packages/anac-certified-attributes-importer/.env index b593a96e2b..405a18517a 100644 --- a/packages/anac-certified-attributes-importer/.env +++ b/packages/anac-certified-attributes-importer/.env @@ -5,14 +5,14 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 - -SFTP_HOST="" -SFTP_PORT=0 -SFTP_USERNAME="" -SFTP_PASSWORD="" + +SFTP_HOST="127.0.0.1" +SFTP_PORT=2022 +SFTP_USERNAME="test" +SFTP_PASSWORD="test" SFTP_FILENAME_PREFIX="" SFTP_PATH="" -FORCE_REMOTE_FILE_NAME="" +FORCE_REMOTE_FILE_NAME="anac-test-csv.csv" INTERNAL_JWT_KID=ffcc9b5b-4612-49b1-9374-9d203a3834f2 INTERNAL_JWT_SUBJECT="dev-refactor.interop-eservice-descriptors-archiver" @@ -22,6 +22,4 @@ INTERNAL_JWT_SECONDS_DURATION=60 TENANT_PROCESS_URL="http://localhost:3500" RECORDS_PROCESS_BATCH_SIZE=5 -ANAC_TENANT_ID="" - -AWS_CONFIG_FILE=aws.config.local +ANAC_TENANT_ID="69e2865e-65ab-4e48-a638-2037a9ee2ee7" diff --git a/packages/anac-certified-attributes-importer/aws.config.local b/packages/anac-certified-attributes-importer/aws.config.local deleted file mode 100644 index 34826a60e2..0000000000 --- a/packages/anac-certified-attributes-importer/aws.config.local +++ /dev/null @@ -1,4 +0,0 @@ -[default] -aws_access_key_id=testawskey -aws_secret_access_key=testawssecret -region=eu-south-1 diff --git a/packages/anac-certified-attributes-importer/src/index.ts b/packages/anac-certified-attributes-importer/src/index.ts index 05f43051ab..46956f9c30 100644 --- a/packages/anac-certified-attributes-importer/src/index.ts +++ b/packages/anac-certified-attributes-importer/src/index.ts @@ -37,3 +37,8 @@ await importAttributes( loggerInstance, correlationId ); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. diff --git a/packages/anac-certified-attributes-importer/src/model/tenant.ts b/packages/anac-certified-attributes-importer/src/model/tenant.ts new file mode 100644 index 0000000000..08af14d753 --- /dev/null +++ b/packages/anac-certified-attributes-importer/src/model/tenant.ts @@ -0,0 +1,11 @@ +import { Tenant } from "pagopa-interop-models"; +import { z } from "zod"; + +export const AnacReadModelTenant = Tenant.pick({ + id: true, + externalId: true, + attributes: true, + features: true, +}); + +export type AnacReadModelTenant = z.infer; diff --git a/packages/anac-certified-attributes-importer/src/service/processor.ts b/packages/anac-certified-attributes-importer/src/service/processor.ts index 41d9c4930a..eb82b0e825 100644 --- a/packages/anac-certified-attributes-importer/src/service/processor.ts +++ b/packages/anac-certified-attributes-importer/src/service/processor.ts @@ -1,7 +1,7 @@ /* eslint-disable max-params */ import { parse } from "csv/sync"; import { Logger, RefreshableInteropToken, zipBy } from "pagopa-interop-commons"; -import { CorrelationId, Tenant } from "pagopa-interop-models"; +import { CorrelationId } from "pagopa-interop-models"; import { AnacAttributes, AttributeIdentifiers, @@ -9,6 +9,7 @@ import { } from "../model/processorModel.js"; import { CsvRow, NonPaRow, PaRow } from "../model/csvRowModel.js"; import { InteropContext } from "../model/interopContextModel.js"; +import { AnacReadModelTenant } from "../model/tenant.js"; import { TenantProcessService } from "./tenantProcessService.js"; import { ReadModelQueries } from "./readmodelQueriesService.js"; import { SftpClient } from "./sftpService.js"; @@ -196,7 +197,9 @@ async function getAttributesIdentifiers( readModel: ReadModelQueries, anacTenantId: string ): Promise { - const anacTenant: Tenant = await readModel.getTenantById(anacTenantId); + const anacTenant: AnacReadModelTenant = await readModel.getTenantById( + anacTenantId + ); const certifier = anacTenant.features.find( (f) => f.type === "PersistentCertifier" ); @@ -253,7 +256,7 @@ const prepareTenantsProcessor = ( async function processTenants( orgs: T[], extractTenantCode: (org: T) => string, - retrieveTenants: (codes: string[]) => Promise + retrieveTenants: (codes: string[]) => Promise ): Promise { if (orgs.length === 0) { return; @@ -344,7 +347,7 @@ const prepareTenantsProcessor = ( async function assignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: AnacReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -371,7 +374,7 @@ async function assignAttribute( async function unassignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: AnacReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -395,7 +398,10 @@ async function unassignAttribute( } } -function tenantContainsAttribute(tenant: Tenant, attributeId: string): boolean { +function tenantContainsAttribute( + tenant: AnacReadModelTenant, + attributeId: string +): boolean { return ( tenant.attributes.find((attribute) => attribute.id === attributeId) !== undefined @@ -404,7 +410,7 @@ function tenantContainsAttribute(tenant: Tenant, attributeId: string): boolean { function getMissingTenants( expectedExternalId: string[], - tenants: Tenant[] + tenants: AnacReadModelTenant[] ): string[] { const existingSet = new Set(tenants.map((t) => t.externalId.value)); diff --git a/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts b/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts index 007811de07..0b379c1fec 100644 --- a/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts +++ b/packages/anac-certified-attributes-importer/src/service/readmodelQueriesService.ts @@ -1,5 +1,6 @@ import { ReadModelRepository } from "pagopa-interop-commons"; -import { Attribute, Tenant } from "pagopa-interop-models"; +import { Attribute } from "pagopa-interop-models"; +import { AnacReadModelTenant } from "../model/tenant.js"; const projectUnrevokedCertifiedAttributes = { _id: 0, @@ -26,7 +27,9 @@ export class ReadModelQueries { /** * Retrieve all PA tenants that matches the given IPA codes, with their unrevoked certified attribute */ - public async getPATenants(ipaCodes: string[]): Promise { + public async getPATenants( + ipaCodes: string[] + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -39,14 +42,16 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); } /** * Retrieve all non-PA tenants that matches the given tax codes, with their unrevoked certified attribute */ - public async getNonPATenants(taxCodes: string[]): Promise { + public async getNonPATenants( + taxCodes: string[] + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -59,11 +64,11 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); } - public async getTenantById(tenantId: string): Promise { + public async getTenantById(tenantId: string): Promise { const result = await this.readModelClient.tenants .aggregate([ { @@ -75,7 +80,7 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); if (result.length === 0) { @@ -111,7 +116,7 @@ export class ReadModelQueries { public async getTenantsWithAttributes( attributeIds: string[] - ): Promise { + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -123,7 +128,7 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => AnacReadModelTenant.parse(data)) .toArray(); } } diff --git a/packages/dtd-catalog-exporter/package.json b/packages/dtd-catalog-exporter/package.json index 4b8cbd21e8..c68f38c705 100644 --- a/packages/dtd-catalog-exporter/package.json +++ b/packages/dtd-catalog-exporter/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "tsx -r 'dotenv-flow/config' ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/packages/ivass-certified-attributes-importer/.env b/packages/ivass-certified-attributes-importer/.env index c50cd35134..10ddd4fccb 100644 --- a/packages/ivass-certified-attributes-importer/.env +++ b/packages/ivass-certified-attributes-importer/.env @@ -5,7 +5,7 @@ READMODEL_DB_NAME="readmodel" READMODEL_DB_USERNAME="root" READMODEL_DB_PASSWORD="example" READMODEL_DB_PORT=27017 - + INTERNAL_JWT_KID=ffcc9b5b-4612-49b1-9374-9d203a3834f2 INTERNAL_JWT_SUBJECT="dev-refactor.interop-eservice-descriptors-archiver" INTERNAL_JWT_ISSUER="dev-refactor.interop.pagopa.it" @@ -14,8 +14,11 @@ INTERNAL_JWT_SECONDS_DURATION=60 TENANT_PROCESS_URL="http://localhost:3500" RECORDS_PROCESS_BATCH_SIZE=5 -IVASS_TENANT_ID="" -SOURCE_URL="" -HISTORY_BUCKET_NAME="" +IVASS_TENANT_ID="69e2865e-65ab-4e48-a638-2037a9ee2ee7" +SOURCE_URL="http://127.0.0.1:8080/ivass-test-csv.zip" +HISTORY_BUCKET_NAME="interop-local-bucket" +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 AWS_CONFIG_FILE=aws.config.local diff --git a/packages/ivass-certified-attributes-importer/src/index.ts b/packages/ivass-certified-attributes-importer/src/index.ts index f33d4e6bc1..a5bd1b0d71 100644 --- a/packages/ivass-certified-attributes-importer/src/index.ts +++ b/packages/ivass-certified-attributes-importer/src/index.ts @@ -47,3 +47,8 @@ await importAttributes( loggerInstance, correlationId ); + +process.exit(0); +// process.exit() should not be required. +// however, something in this script hangs on exit. +// TODO figure out why and remove this workaround. diff --git a/packages/ivass-certified-attributes-importer/src/model/tenant.ts b/packages/ivass-certified-attributes-importer/src/model/tenant.ts new file mode 100644 index 0000000000..bd62828821 --- /dev/null +++ b/packages/ivass-certified-attributes-importer/src/model/tenant.ts @@ -0,0 +1,11 @@ +import { Tenant } from "pagopa-interop-models"; +import { z } from "zod"; + +export const IvassReadModelTenant = Tenant.pick({ + id: true, + externalId: true, + attributes: true, + features: true, +}); + +export type IvassReadModelTenant = z.infer; diff --git a/packages/ivass-certified-attributes-importer/src/service/processor.ts b/packages/ivass-certified-attributes-importer/src/service/processor.ts index bf29bddebf..7bad3510e0 100644 --- a/packages/ivass-certified-attributes-importer/src/service/processor.ts +++ b/packages/ivass-certified-attributes-importer/src/service/processor.ts @@ -1,10 +1,6 @@ /* eslint-disable max-params */ import { Logger, RefreshableInteropToken } from "pagopa-interop-commons"; -import { - CorrelationId, - Tenant, - TenantFeatureCertifier, -} from "pagopa-interop-models"; +import { CorrelationId, TenantFeatureCertifier } from "pagopa-interop-models"; import { parse } from "csv/sync"; import { AttributeIdentifiers, @@ -14,6 +10,7 @@ import { import { IVASS_INSURANCES_ATTRIBUTE_CODE } from "../config/constants.js"; import { CsvRow, RawCsvRow } from "../model/csvRowModel.js"; import { InteropContext } from "../model/interopContextModel.js"; +import { IvassReadModelTenant } from "../model/tenant.js"; import { ReadModelQueries } from "./readModelQueriesService.js"; import { TenantProcessService } from "./tenantProcessService.js"; @@ -167,7 +164,9 @@ async function getAttributesIdentifiers( readModel: ReadModelQueries, ivassTenantId: string ): Promise { - const ivassTenant: Tenant = await readModel.getTenantById(ivassTenantId); + const ivassTenant: IvassReadModelTenant = await readModel.getTenantById( + ivassTenantId + ); const certifier = ivassTenant.features.find( (f) => f.type === "PersistentCertifier" ); @@ -199,7 +198,7 @@ const isAttributeAssigned = (org: CsvRow, now: number): boolean => async function assignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: IvassReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -226,7 +225,7 @@ async function assignAttribute( async function unassignAttribute( tenantProcess: TenantProcessService, refreshableToken: RefreshableInteropToken, - tenant: Tenant, + tenant: IvassReadModelTenant, attribute: AttributeIdentifiers, logger: Logger, correlationId: CorrelationId @@ -250,7 +249,10 @@ async function unassignAttribute( } } -function tenantContainsAttribute(tenant: Tenant, attributeId: string): boolean { +function tenantContainsAttribute( + tenant: IvassReadModelTenant, + attributeId: string +): boolean { return ( tenant.attributes.find((attribute) => attribute.id === attributeId) !== undefined diff --git a/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts b/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts index b6c853b478..61cadf6e2d 100644 --- a/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts +++ b/packages/ivass-certified-attributes-importer/src/service/readModelQueriesService.ts @@ -1,5 +1,6 @@ import { ReadModelRepository } from "pagopa-interop-commons"; -import { Attribute, Tenant } from "pagopa-interop-models"; +import { Attribute } from "pagopa-interop-models"; +import { IvassReadModelTenant } from "../model/tenant.js"; const projectUnrevokedCertifiedAttributes = { _id: 0, @@ -26,7 +27,9 @@ export class ReadModelQueries { /** * Retrieve tenants that match the given tax codes, with their unrevoked certified attribute */ - public async getIVASSTenants(externalId: string[]): Promise { + public async getIVASSTenants( + externalId: string[] + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -39,13 +42,13 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => IvassReadModelTenant.parse(data)) .toArray(); } public async getTenantsWithAttributes( attributeIds: string[] - ): Promise { + ): Promise { return await this.readModelClient.tenants .aggregate([ { @@ -57,11 +60,11 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => IvassReadModelTenant.parse(data)) .toArray(); } - public async getTenantById(tenantId: string): Promise { + public async getTenantById(tenantId: string): Promise { const result = await this.readModelClient.tenants .aggregate([ { @@ -73,7 +76,7 @@ export class ReadModelQueries { $project: projectUnrevokedCertifiedAttributes, }, ]) - .map(({ data }) => Tenant.parse(data)) + .map(({ data }) => IvassReadModelTenant.parse(data)) .toArray(); if (result.length === 0) { diff --git a/packages/one-trust-notices/.env b/packages/one-trust-notices/.env index 94054af2e8..258059f0f2 100644 --- a/packages/one-trust-notices/.env +++ b/packages/one-trust-notices/.env @@ -1,6 +1,4 @@ LOG_LEVEL=info - -AWS_REGION=eu-south-1 LANGS=it,de,fr,es CONTENT_STORAGE_BUCKET=one-trust-notices-content @@ -16,3 +14,5 @@ PRIVACY_NOTICES_DYNAMO_TABLE_NAME=one-trust-notices S3_CUSTOM_SERVER=true S3_SERVER_HOST=http://localhost S3_SERVER_PORT=9000 +AWS_CONFIG_FILE=aws.config.local +AWS_REGION=eu-south-1 diff --git a/packages/one-trust-notices/aws.config.local b/packages/one-trust-notices/aws.config.local index 06a4c18070..0414a88c2b 100644 --- a/packages/one-trust-notices/aws.config.local +++ b/packages/one-trust-notices/aws.config.local @@ -2,3 +2,7 @@ aws_access_key_id=testawskey aws_secret_access_key=testawskey region=eu-south-1 + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 From 70331043dffae6a892cc670ba72c2977336f1901 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 7 Nov 2024 17:48:55 +0100 Subject: [PATCH 020/126] IMN-794 Add events service v1 in authorization-platformstate-writer (#1063) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 427 ++- .../src/consumerServiceV2.ts | 7 +- .../src/utils.ts | 17 +- .../consumerServiceV1.integration.test.ts | 2733 +++++++++++++++++ .../test/utils.test.ts | 13 +- 5 files changed, 3176 insertions(+), 21 deletions(-) create mode 100644 packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 78b5d36e02..bc0af7806f 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -1,20 +1,412 @@ import { match } from "ts-pattern"; -import { AuthorizationEventEnvelopeV1 } from "pagopa-interop-models"; +import { + AuthorizationEventEnvelopeV1, + Client, + ClientId, + ClientV1, + fromClientV1, + fromKeyV1, + genericInternalError, + itemState, + Key, + KeyV1, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesClientPK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + missingKafkaMessageDataError, + PlatformStatesClientEntry, + PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + unsafeBrandId, +} from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + clientKindToTokenGenerationStatesClientKind, + convertEntriesToClientKidInTokenGenerationStates, + createTokenClientPurposeEntry, + deleteClientEntryFromPlatformStates, + deleteClientEntryFromTokenGenerationStatesTable, + deleteEntriesFromTokenStatesByClient, + deleteEntriesFromTokenStatesByKid, + deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + extractAgreementIdFromAgreementPK, + extractKidFromTokenEntryPK, + readClientEntriesInTokenGenerationStates, + readClientEntry, + retrievePlatformStatesByPurpose, + setClientPurposeIdsInPlatformStatesEntry, + updateTokenDataForSecondRetrieval, + upsertPlatformClientEntry, + upsertTokenClientKidEntry, + upsertTokenStateClientPurposeEntry, +} from "./utils.js"; export async function handleMessageV1( message: AuthorizationEventEnvelopeV1, - _dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient ): Promise { await match(message) + .with({ type: "ClientAdded" }, async (msg) => { + const client = parseClient(msg.data.client, msg.type); + + const pk = makePlatformStatesClientPK(client.id); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry && clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const updatedClientEntry: PlatformStatesClientEntry = { + PK: pk, + version: msg.version, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + }; + await upsertPlatformClientEntry(updatedClientEntry, dynamoDBClient); + } + }) + .with({ type: "KeysAdded" }, async (msg) => { + const keyV1 = msg.data.keys[0].value; + + const clientId = unsafeBrandId(msg.data.clientId); + const key = parseKey(keyV1, msg.type); + const kid = key.kid; + const pem = key.encodedPem; + + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (!clientEntry || clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const clientPurposesIds = clientEntry?.clientPurposesIds || []; + + const platformClientEntry: PlatformStatesClientEntry = { + PK: pk, + state: itemState.active, + clientKind: clientEntry.clientKind, + clientConsumerId: clientEntry.clientConsumerId, + clientPurposesIds, + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + + if (clientPurposesIds.length > 0) { + const addedEntries = await Promise.all( + clientPurposesIds.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + PK: tokenClientKidPurposePK, + consumerId: platformClientEntry.clientConsumerId, + clientKind: platformClientEntry.clientKind, + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...((purposeEntry && { + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: platformClientEntry.clientConsumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + }) || + {}), + ...((purposeEntry && + agreementEntry && { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + }) || + {}), + ...((catalogEntry && { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + }) || + {}), + }; + + await upsertTokenStateClientPurposeEntry( + clientKidPurposeEntry, + dynamoDBClient + ); + return clientKidPurposeEntry; + }) + ); + + // Second check for updated fields + await Promise.all( + clientPurposesIds.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const addedClientKidPurposeEntry = addedEntries[index]; + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry: addedClientKidPurposeEntry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } else { + const clientKidEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }), + consumerId: platformClientEntry.clientConsumerId, + clientKind: platformClientEntry.clientKind, + publicKey: pem, + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + updatedAt: new Date().toISOString(), + }; + await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + } + } + }) + .with({ type: "KeyDeleted" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (!clientEntry || clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const platformClientEntry: PlatformStatesClientEntry = { + PK: pk, + state: itemState.active, + clientKind: clientEntry.clientKind, + clientConsumerId: clientEntry.clientConsumerId, + clientPurposesIds: clientEntry.clientPurposesIds, + version: msg.version, + updatedAt: new Date().toISOString(), + }; + await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + + const GSIPK_kid = makeGSIPKKid(msg.data.keyId); + await deleteEntriesFromTokenStatesByKid(GSIPK_kid, dynamoDBClient); + } + }) + .with({ type: "ClientPurposeAdded" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const unparsedPurposeId = msg.data.statesChain?.purpose?.purposeId; + + if (!unparsedPurposeId) { + throw missingKafkaMessageDataError("purposeId", msg.type); + } + const purposeId = unsafeBrandId(unparsedPurposeId); + + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + if (!clientEntry || clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const purposeIds = [...clientEntry.clientPurposesIds, purposeId]; + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: pk, + version: msg.version, + clientPurposeIds: purposeIds, + }, + dynamoDBClient + ); + } + + const GSIPK_clientId = clientId; + const tokenClientEntries = await readClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + if (tokenClientEntries.length === 0) { + return Promise.resolve(); + } else { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const seenKids = new Set(); + const addedTokenClientPurposeEntries = await Promise.all( + tokenClientEntries.map(async (entry) => { + const parsedTokenClientEntry = + TokenGenerationStatesClientEntry.safeParse(entry); + const parsedTokenClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.safeParse(entry); + + if (parsedTokenClientEntry.success) { + const newTokenClientPurposeEntry = createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientEntry.data, + kid: extractKidFromTokenEntryPK(parsedTokenClientEntry.data.PK), + clientId, + consumerId: parsedTokenClientEntry.data.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + await deleteClientEntryFromTokenGenerationStatesTable( + entry, + dynamoDBClient + ); + return newTokenClientPurposeEntry; + } + + if (parsedTokenClientPurposeEntry.success) { + const kid = extractKidFromTokenEntryPK( + parsedTokenClientPurposeEntry.data.PK + ); + if (!seenKids.has(kid)) { + const newTokenClientPurposeEntry = + createTokenClientPurposeEntry({ + tokenEntry: parsedTokenClientPurposeEntry.data, + kid, + clientId, + consumerId: parsedTokenClientPurposeEntry.data.consumerId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenStateClientPurposeEntry( + newTokenClientPurposeEntry, + dynamoDBClient + ); + seenKids.add(kid); + return newTokenClientPurposeEntry; + } + } + + throw genericInternalError(`Unable to parse ${entry}`); + }) + ); + + // Second check for updated fields + await Promise.all( + addedTokenClientPurposeEntries.map(async (entry) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + await updateTokenDataForSecondRetrieval({ + dynamoDBClient, + entry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } + }) + .with({ type: "ClientPurposeRemoved" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const purposeIdToRemove = unsafeBrandId(msg.data.purposeId); + + const pk = makePlatformStatesClientPK(clientId); + const clientEntry = await readClientEntry(pk, dynamoDBClient); + + if (clientEntry) { + if (clientEntry.version > msg.version) { + return Promise.resolve(); + } else { + const updatedPurposeIds = clientEntry.clientPurposesIds.filter( + (purposeId) => purposeId !== purposeIdToRemove + ); + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId, + purposeId: unsafeBrandId(msg.data.purposeId), + }); + + // platform-states + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: pk, + version: msg.version, + clientPurposeIds: updatedPurposeIds, + }, + dynamoDBClient + ); + + // token-generation-states + if (updatedPurposeIds.length > 0) { + await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } else { + await convertEntriesToClientKidInTokenGenerationStates( + GSIPK_clientId_purposeId, + dynamoDBClient + ); + } + } + } + }) + .with({ type: "ClientDeleted" }, async (msg) => { + const clientId = unsafeBrandId(msg.data.clientId); + const pk = makePlatformStatesClientPK(clientId); + await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); + + const GSIPK_clientId = clientId; + await deleteEntriesFromTokenStatesByClient( + GSIPK_clientId, + dynamoDBClient + ); + }) .with( - { type: "ClientAdded" }, - { type: "ClientDeleted" }, - { type: "ClientPurposeAdded" }, - { type: "ClientPurposeRemoved" }, - { type: "KeyDeleted" }, { type: "KeyRelationshipToUserMigrated" }, - { type: "KeysAdded" }, { type: "RelationshipAdded" }, { type: "RelationshipAdded" }, { type: "RelationshipRemoved" }, @@ -24,3 +416,22 @@ export async function handleMessageV1( ) .exhaustive(); } + +export const parseKey = (keyV1: KeyV1 | undefined, eventType: string): Key => { + if (!keyV1) { + throw missingKafkaMessageDataError("key", eventType); + } + + return fromKeyV1(keyV1); +}; + +const parseClient = ( + clientV1: ClientV1 | undefined, + eventType: string +): Client => { + if (!clientV1) { + throw missingKafkaMessageDataError("client", eventType); + } + + return fromClientV1(clientV1); +}; diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 4fd99439fa..b4cc629627 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -30,7 +30,6 @@ import { deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, readClientEntry, readClientEntriesInTokenGenerationStates, - cleanClientPurposeIdsInPlatformStatesEntry, deleteClientEntryFromTokenGenerationStatesTable, extractKidFromTokenEntryPK, extractAgreementIdFromAgreementPK, @@ -38,6 +37,7 @@ import { upsertPlatformClientEntry, upsertTokenClientKidEntry, upsertTokenStateClientPurposeEntry, + setClientPurposeIdsInPlatformStatesEntry, updateTokenDataForSecondRetrieval, createTokenClientPurposeEntry, } from "./utils.js"; @@ -346,9 +346,8 @@ export async function handleMessageV2( }); // platform-states - await cleanClientPurposeIdsInPlatformStatesEntry( - pk, - msg.version, + await setClientPurposeIdsInPlatformStatesEntry( + { primaryKey: pk, version: msg.version, clientPurposeIds: [] }, dynamoDBClient ); diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 9659d82c7c..1b81553deb 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -716,9 +716,16 @@ export const readClientEntriesInTokenGenerationStates = async ( return await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); }; -export const cleanClientPurposeIdsInPlatformStatesEntry = async ( - primaryKey: PlatformStatesClientPK, - version: number, +export const setClientPurposeIdsInPlatformStatesEntry = async ( + { + primaryKey, + version, + clientPurposeIds, + }: { + primaryKey: PlatformStatesClientPK; + version: number; + clientPurposeIds: PurposeId[]; + }, dynamoDBClient: DynamoDBClient ): Promise => { const input: UpdateItemInput = { @@ -730,7 +737,9 @@ export const cleanClientPurposeIdsInPlatformStatesEntry = async ( }, ExpressionAttributeValues: { ":clientPurposesIds": { - L: [], + L: clientPurposeIds.map((purposeId) => ({ + S: purposeId, + })), }, ":newVersion": { N: version.toString(), diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts new file mode 100644 index 0000000000..6da35e8c62 --- /dev/null +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -0,0 +1,2733 @@ +import { fail } from "assert"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockClient, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + readAllPlatformStateItems, + readAllTokenStateItems, + getMockPurpose, + getMockPurposeVersion, + writePlatformPurposeEntry, + getMockAgreement, + writePlatformAgreementEntry, + getMockDescriptor, + writeCatalogEntry, + getMockKey, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { + AuthorizationEventEnvelope, + Client, + ClientAddedV1, + ClientComponentStateV1, + ClientDeletedV1, + ClientId, + ClientPurposeAddedV1, + ClientPurposeRemovedV1, + Descriptor, + generateId, + itemState, + KeyDeletedV1, + KeysAddedV1, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeId, + purposeVersionState, + TenantId, + toClientV1, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + toKeyV1, +} from "pagopa-interop-models"; +import { handleMessageV1 } from "../src/consumerServiceV1.js"; +import { + clientKindToTokenGenerationStatesClientKind, + readClientEntry, + writeClientEntry, + writeTokenStateClientEntry, +} from "../src/utils.js"; +import { config } from "./utils.js"; + +describe("integration tests V1 events", async () => { + if (!config) { + fail(); + } + const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, + }); + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("ClientAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const client = getMockClient(); + + const payload: ClientAddedV1 = { + client: toClientV1(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + }); + + it("should add the entry if it doesn't exist", async () => { + const messageVersion = 1; + + const client = getMockClient(); + + const payload: ClientAddedV1 = { + client: toClientV1(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: messageVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformClientEntry); + }); + + it("should update the entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const client = getMockClient(); + + const payload: ClientAddedV1 = { + client: toClientV1(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: messageVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: client.purposes, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformClientEntry); + }); + }); + + describe("KeysAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [key], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(key), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [generateId()], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the existing table entry doesn't exist", async () => { + const messageVersion = 2; + + const key = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [key], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(key), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(key.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid-purpose entries", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + consumerId, + keys: [oldKey, addedKey], + purposes: [purpose1.id, purpose2.id], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id, purpose2.id], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: oldKey.kid, + purposeId: purpose2.id, + }); + const tokenClientKidPurposePK3 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK4 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const gsiPKClientIdPurposeId3 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId4 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK3), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK4), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose1.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry1.GSIPK_consumerId_eserviceId, + agreementId: agreement1.id, + agreementState: platformAgreementEntry1.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + descriptorState: previousDescriptorEntry1.state, + descriptorAudience: previousDescriptorEntry1.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry1.descriptorVoucherLifespan, + GSIPK_purposeId: purpose1.id, + purposeState: platformPurposeEntry1.state, + purposeVersionId: platformPurposeEntry1.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }), + updatedAt: new Date().toISOString(), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: addedKey.kid, + purposeId: purpose2.id, + }), + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + publicKey: addedKey.encodedPem, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(4); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and insert token-generation-states client-kid entry if the client does not contain purposes", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + ); + }); + + it("should update platform-states entry and update token-generation-states client-kid entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const oldKey = getMockKey(); + const addedKey = getMockKey(); + const client: Client = { + ...getMockClient(), + keys: [oldKey, addedKey], + }; + + const payload: KeysAddedV1 = { + clientId: client.id, + keys: [ + { + keyId: generateId(), + value: toKeyV1(addedKey), + }, + ], + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeysAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: oldKey.kid, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }); + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + }; + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: addedKey.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: addedKey.encodedPem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry1, expectedTokenClientEntry]) + ); + }); + }); + + describe("KeyDeleted", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: KeyDeletedV1 = { + clientId: client.id, + keyId: kidToRemove, + deactivationTimestamp: new Date().toISOString(), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeyDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kidToRemove, + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kidToRemove), + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should insert platform-states entry and delete token-generation-states entries for that kid", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + + const payload: KeyDeletedV1 = { + clientId: client.id, + keyId: kidToRemove, + deactivationTimestamp: new Date().toISOString(), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeyDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + const tokenClientPurposeEntryWithOtherKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(), + GSIPK_clientId: client.id, + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformStatesEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntryWithOtherKid, + tokenClientPurposeEntryWithKid, + ]) + ); + }); + + it("should update platform-states entry and delete token-generation-states entries for that kid", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + const kidToRemove = "removed kid"; + const otherKid = "other kid"; + + const payload: KeyDeletedV1 = { + clientId: client.id, + keyId: kidToRemove, + deactivationTimestamp: new Date().toISOString(), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "KeyDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kidToRemove, + purposeId, + }); + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: otherKid, + }); + + const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + + const tokenClientEntryWithOtherKid: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(otherKid), + }; + + await writeTokenStateEntry( + tokenClientPurposeEntryWithKid, + dynamoDBClient + ); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherKid, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntryWithOtherKid]); + }); + }); + + describe("ClientPurposeAdded", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the entry doesn't exist in platform-states", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = + getMockTokenStatesClientPurposeEntry(); + const tokenClientEntry: TokenGenerationStatesClientEntry = + getMockTokenStatesClientEntry(); + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purposeId], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientPurposeEntry, tokenClientEntry]) + ); + }); + + it("should update platform-states entry and convert client-kid entries to client-kid-purpose entries in token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose.id], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId: purpose.id, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry, dynamoDBClient); + + const agreement = getMockAgreement(); + const platformAgreementEntry: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + await writePlatformAgreementEntry(platformAgreementEntry, dynamoDBClient); + + const descriptor: Descriptor = { + ...getMockDescriptor(), + id: agreement.descriptorId, + }; + const previousDescriptorEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid1, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: kid2, + }); + + const tokenClientEntry1: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK1), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + }; + const tokenClientEntry2: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK2), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + }; + const tokenClientPurposeEntryWithOtherClient = + getMockTokenStatesClientPurposeEntry(); + + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + await writeTokenStateEntry( + tokenClientPurposeEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purpose.id], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: platformPurposeEntry.state, + purposeVersionId: platformPurposeEntry.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry.GSIPK_consumerId_eserviceId, + agreementId: agreement.id, + agreementState: platformAgreementEntry.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: descriptor.id, + }), + descriptorState: previousDescriptorEntry.state, + descriptorAudience: previousDescriptorEntry.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(3); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientPurposeEntryWithOtherClient, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and add client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId: purpose2.id, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purpose1.id, purpose2.id], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + + it("should update platform-states entry and update client-kid-purpose entries to token-generation-states table", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const consumerId = generateId(); + const purpose1: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purpose2: Purpose = { + ...getMockPurpose(), + consumerId, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + + const client: Client = { + ...getMockClient(), + consumerId, + purposes: [purpose1.id, purpose2.id], + }; + + const payload: ClientPurposeAddedV1 = { + clientId: client.id, + statesChain: { + id: generateId(), + purpose: { + purposeId: purpose2.id, + state: ClientComponentStateV1.ACTIVE, + versionId: generateId(), + }, + }, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry1, dynamoDBClient); + + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: makePlatformStatesPurposePK(purpose2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + }; + await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); + + const agreement1 = getMockAgreement(); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement1.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: purpose1.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement1.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry1, + dynamoDBClient + ); + + const agreement2 = getMockAgreement(); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: makePlatformStatesAgreementPK(agreement2.id), + version: 1, + state: itemState.active, + updatedAt: new Date().toISOString(), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: purpose2.eserviceId, + }), + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: agreement2.descriptorId, + }; + await writePlatformAgreementEntry( + platformAgreementEntry2, + dynamoDBClient + ); + + const descriptor1: Descriptor = { + ...getMockDescriptor(), + id: agreement1.descriptorId, + }; + const previousDescriptorEntry1: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose1.eserviceId, + descriptorId: descriptor1.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + + const descriptor2: Descriptor = { + ...getMockDescriptor(), + id: agreement2.descriptorId, + }; + const previousDescriptorEntry2: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purpose1.id], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const kid1 = "KID1"; + const kid2 = "KID2"; + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose1.id, + }); + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose1.id, + }); + const tokenClientKidPK3 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid1, + purposeId: purpose2.id, + }); + const tokenClientKidPK4 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: kid2, + purposeId: purpose2.id, + }); + + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose1.id, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }); + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK3), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK4), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + await writeTokenStateClientEntry( + tokenClientEntryWithOtherClient, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + version: messageVersion, + clientPurposesIds: [purpose1.id, purpose2.id], + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + const newTokenClientPurposeEntryData = { + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose2.id, + }), + GSIPK_purposeId: purpose2.id, + purposeState: platformPurposeEntry2.state, + purposeVersionId: platformPurposeEntry2.purposeVersionId, + GSIPK_consumerId_eserviceId: + platformAgreementEntry2.GSIPK_consumerId_eserviceId, + agreementId: agreement2.id, + agreementState: platformAgreementEntry2.state, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose2.eserviceId, + descriptorId: descriptor2.id, + }), + descriptorState: previousDescriptorEntry2.state, + descriptorAudience: previousDescriptorEntry2.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry2.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + + const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry1, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid1, + }), + }; + const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...tokenClientPurposeEntry2, + ...newTokenClientPurposeEntryData, + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + purposeId: purpose2.id, + kid: kid2, + }), + }; + + expect(retrievedTokenEntries).toHaveLength(5); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([ + tokenClientEntryWithOtherClient, + tokenClientPurposeEntry1, + tokenClientPurposeEntry2, + expectedTokenClientPurposeEntry1, + expectedTokenClientPurposeEntry2, + ]) + ); + }); + }); + + describe("ClientPurposeRemoved", () => { + it("should do no operation if the existing table entry is more recent", async () => { + const previousPlatformEntryVersion = 2; + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV1 = { + purposeId, + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should do no operation if the purpose platform-states entry doesn't exist and the token-generation-states entries aren't associated to the purpose id in the message", async () => { + const messageVersion = 1; + + const purposeId = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId], + }; + + const payload: ClientPurposeRemovedV1 = { + purposeId, + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + expect( + await readClientEntry(platformClientPK, dynamoDBClient) + ).toBeUndefined(); + + // token-generation-states + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: "KID", + }); + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + GSIPK_clientId: client.id, + }; + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformClientEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + expect(retrievedPlatformClientEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + }); + + it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const purposeId1 = generateId(); + const purposeId2 = generateId(); + const client: Client = { + ...getMockClient(), + purposes: [purposeId1], + }; + + const payload: ClientPurposeRemovedV1 = { + purposeId: purposeId2, + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [purposeId1, purposeId2], + }; + await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + + // token-generation-states + const mockClientKidPurpose1 = "mockClientKidPurpose1"; + const mockClientKidPurpose2 = "mockClientKidPurpose2"; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose1, + purposeId: purposeId1, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockClientKidPurpose2, + purposeId: purposeId2, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId1, + }); + const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purposeId2, + }); + + const tokenClientEntry = getMockTokenStatesClientEntry(); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_purposeId: purposeId1, + }; + + const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_purposeId: purposeId2, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntry = await readClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [purposeId1], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toHaveLength(2); + expect(retrievedTokenEntries).toEqual( + expect.arrayContaining([tokenClientEntry, tokenClientPurposeEntry1]) + ); + }); + }); + + describe("ClientDeleted", () => { + it("should delete platform-states entry and token-generation-states entries", async () => { + const messageVersion = 2; + + const purposeId = generateId(); + const client = getMockClient(); + + const payload: ClientDeletedV1 = { + clientId: client.id, + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientDeleted", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + const otherClientId = generateId(); + + // platform-states + const pk1PlatformStates = makePlatformStatesClientPK(client.id); + const clientPlatformStateEntry1: PlatformStatesClientEntry = { + PK: pk1PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + + const pk2PlatformStates = makePlatformStatesClientPK(otherClientId); + const clientPlatformStateEntry2: PlatformStatesClientEntry = { + PK: pk2PlatformStates, + version: 1, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [], + }; + await writeClientEntry(clientPlatformStateEntry1, dynamoDBClient); + await writeClientEntry(clientPlatformStateEntry2, dynamoDBClient); + + // token-generation-states + const pkTokenStates1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: "kid", + purposeId, + }); + + const pkTokenStates2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: otherClientId, + kid: "kid", + purposeId, + }); + + const clientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates1), + GSIPK_clientId: client.id, + }; + + const otherClientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(pkTokenStates2), + GSIPK_clientId: otherClientId, + }; + + await writeTokenStateEntry(clientPurposeTokenStateEntry, dynamoDBClient); + await writeTokenStateEntry( + otherClientPurposeTokenStateEntry, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient); + + // platform-states + const retrievedPlatformStatesEntries = await readAllPlatformStateItems( + dynamoDBClient + ); + expect(retrievedPlatformStatesEntries).toEqual([ + clientPlatformStateEntry2, + ]); + + // token-generation-states + const retrievedTokenEntries = await readAllTokenStateItems( + dynamoDBClient + ); + expect(retrievedTokenEntries).toEqual([ + otherClientPurposeTokenStateEntry, + ]); + }); + }); +}); diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 7d3e7b2b48..477a2bf32c 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -70,7 +70,7 @@ import { import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { z } from "zod"; import { - cleanClientPurposeIdsInPlatformStatesEntry, + setClientPurposeIdsInPlatformStatesEntry, convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, deleteClientEntryFromTokenGenerationStatesTable, @@ -470,7 +470,7 @@ describe("utils", () => { ); }); - it("cleanClientPurposeIdsInPlatformStatesEntry", async () => { + it("setClientPurposeIdsInPlatformStatesEntry", async () => { const clientEntry1: PlatformStatesClientEntry = { ...getMockPlatformStatesClientEntry(), clientPurposesIds: [generateId(), generateId()], @@ -483,9 +483,12 @@ describe("utils", () => { await writeClientEntry(clientEntry1, dynamoDBClient); await writeClientEntry(clientEntry2, dynamoDBClient); - await cleanClientPurposeIdsInPlatformStatesEntry( - clientEntry1.PK, - clientEntry1.version + 1, + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: clientEntry1.PK, + version: clientEntry1.version + 1, + clientPurposeIds: [], + }, dynamoDBClient ); From 4a4775ea3d1c9ab32ebe5af9bb00850491aa3e55 Mon Sep 17 00:00:00 2001 From: Stefano Perazzolo <13318704+beetlecrunch@users.noreply.github.com> Date: Mon, 11 Nov 2024 10:08:59 +0100 Subject: [PATCH 021/126] selfcare-onboarding-consumer: add new institution type (#1186) --- .../src/services/selfcareOnboardingProcessor.ts | 1 + .../test/selfcareOnboardingProcessor.test.ts | 4 ++-- packages/selfcare-onboarding-consumer/test/utils.ts | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts b/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts index d236cbdd42..3a6e064af5 100644 --- a/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts +++ b/packages/selfcare-onboarding-consumer/src/services/selfcareOnboardingProcessor.ts @@ -64,6 +64,7 @@ export function selfcareOnboardingProcessorBuilder( const origin = match(institution.institutionType) .with("SCP", () => `${institution.origin}-SCP`) .with("PRV", () => `${institution.origin}-PRV`) + .with("PT", () => `${institution.origin}-PT`) .otherwise(() => institution.origin); if (!allowedOrigins.includes(origin)) { diff --git a/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts b/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts index a815ef2c6a..ff21adafcb 100644 --- a/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts +++ b/packages/selfcare-onboarding-consumer/test/selfcareOnboardingProcessor.test.ts @@ -264,10 +264,10 @@ describe("Message processor", () => { }) ); }); - it.each(["SCP", "PRV"])( + it.each(["SCP", "PRV", "PT"])( "should upsert tenant with institutionType %s correctly", async (institutionType) => { - const origin = "PDND_INFOCAMERE"; + const origin = "INFOCAMERE"; const message: EachMessagePayload = { ...kafkaMessagePayload, diff --git a/packages/selfcare-onboarding-consumer/test/utils.ts b/packages/selfcare-onboarding-consumer/test/utils.ts index ef4a19f5a5..2e4b766fe2 100644 --- a/packages/selfcare-onboarding-consumer/test/utils.ts +++ b/packages/selfcare-onboarding-consumer/test/utils.ts @@ -8,8 +8,9 @@ export const allowedOrigins = [ "IPA", "ANAC", "IVASS", - "PDND_INFOCAMERE-PRV", - "PDND_INFOCAMERE-SCP", + "INFOCAMERE-PRV", + "INFOCAMERE-SCP", + "INFOCAMERE-PT", ]; export const selfcareUpsertTenantMock = (): Promise => From b7877b818f46b97e9f0538a13f40785e3ee3552c Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Mon, 11 Nov 2024 11:28:24 +0100 Subject: [PATCH 022/126] IMN-518 pt1 - Case insensitive duplicate checks for catalog (#1137) --- .../src/services/catalogService.ts | 8 ++++++-- .../test/cloneDescriptor.test.ts | 17 ++++++++++++----- .../catalog-process/test/createEService.test.ts | 11 +++++++---- .../test/createRiskAnalysis.test.ts | 13 +++++++++---- .../catalog-process/test/updateDocument.test.ts | 8 ++++---- .../catalog-process/test/updateEservice.test.ts | 6 +++--- .../test/updateRiskAnalysis.test.ts | 14 ++++++++++---- .../catalog-process/test/uploadDocument.test.ts | 8 ++++---- 8 files changed, 55 insertions(+), 30 deletions(-) diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 648122835c..e5ecf618e1 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -653,7 +653,10 @@ export function catalogServiceBuilder( if ( document.kind === "DOCUMENT" && - descriptor.docs.some((d) => d.prettyName === document.prettyName) + descriptor.docs.some( + (d) => + d.prettyName.toLowerCase() === document.prettyName.toLowerCase() + ) ) { throw prettyNameDuplicate(document.prettyName, descriptor.id); } @@ -811,7 +814,8 @@ export function catalogServiceBuilder( descriptor.docs.some( (d) => d.id !== documentId && - d.prettyName === apiEServiceDescriptorDocumentUpdateSeed.prettyName + d.prettyName.toLowerCase() === + apiEServiceDescriptorDocumentUpdateSeed.prettyName.toLowerCase() ) ) { throw prettyNameDuplicate( diff --git a/packages/catalog-process/test/cloneDescriptor.test.ts b/packages/catalog-process/test/cloneDescriptor.test.ts index 41dab1bda6..5031a026b6 100644 --- a/packages/catalog-process/test/cloneDescriptor.test.ts +++ b/packages/catalog-process/test/cloneDescriptor.test.ts @@ -242,7 +242,7 @@ describe("clone descriptor", () => { }) ).rejects.toThrowError(FileManagerError); }); - it("should throw eServiceDuplicate if an eservice with the same name already exists", async () => { + it("should throw eServiceDuplicate if an eservice with the same name already exists, case insensitive", async () => { const descriptor: Descriptor = { ...mockDescriptor, state: descriptorState.draft, @@ -251,15 +251,16 @@ describe("clone descriptor", () => { }; const eservice1: EService = { ...mockEService, + name: mockEService.name.toUpperCase(), id: generateId(), descriptors: [descriptor], }; await addOneEService(eservice1); const cloneTimestamp = new Date(); - const conflictEServiceName = `${ - eservice1.name - } - clone - ${formatDateddMMyyyyHHmmss(cloneTimestamp)}`; + const conflictEServiceName = `${eservice1.name.toLowerCase()} - clone - ${formatDateddMMyyyyHHmmss( + cloneTimestamp + )}`; const eservice2: EService = { ...mockEService, @@ -276,7 +277,13 @@ describe("clone descriptor", () => { serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError(eServiceDuplicate(conflictEServiceName)); + ).rejects.toThrowError( + eServiceDuplicate( + `${eservice1.name} - clone - ${formatDateddMMyyyyHHmmss( + cloneTimestamp + )}` + ) + ); }); it("should throw eServiceNotFound if the eservice doesn't exist", () => { expect( diff --git a/packages/catalog-process/test/createEService.test.ts b/packages/catalog-process/test/createEService.test.ts index b7f7a30764..8b16fcfe35 100644 --- a/packages/catalog-process/test/createEService.test.ts +++ b/packages/catalog-process/test/createEService.test.ts @@ -120,12 +120,15 @@ describe("create eservice", () => { ); }); - it("should throw eServiceDuplicate if an eservice with the same name already exists", async () => { - await addOneEService(mockEService); + it("should throw eServiceDuplicate if an eservice with the same name already exists, case insensitive", async () => { + await addOneEService({ + ...mockEService, + name: mockEService.name.toUpperCase(), + }); expect( catalogService.createEService( { - name: mockEService.name, + name: mockEService.name.toLowerCase(), description: mockEService.description, technology: "REST", mode: "DELIVER", @@ -138,7 +141,7 @@ describe("create eservice", () => { logger: genericLogger, } ) - ).rejects.toThrowError(eServiceDuplicate(mockEService.name)); + ).rejects.toThrowError(eServiceDuplicate(mockEService.name.toLowerCase())); }); it("should throw originNotCompliant if the requester externalId origin is not allowed", async () => { diff --git a/packages/catalog-process/test/createRiskAnalysis.test.ts b/packages/catalog-process/test/createRiskAnalysis.test.ts index d4c68c1c85..595fde0328 100644 --- a/packages/catalog-process/test/createRiskAnalysis.test.ts +++ b/packages/catalog-process/test/createRiskAnalysis.test.ts @@ -281,7 +281,7 @@ describe("create risk analysis", () => { ) ).rejects.toThrowError(tenantKindNotFound(producer.id)); }); - it("should throw riskAnalysisDuplicated if risk analysis name is duplicated", async () => { + it("should throw riskAnalysisDuplicated if risk analysis name is duplicated, case insensitive", async () => { const producerTenantKind: TenantKind = randomArrayItem( Object.values(tenantKind) ); @@ -304,13 +304,18 @@ describe("create risk analysis", () => { state: descriptorState.draft, }, ], - riskAnalysis: [riskAnalysis], + riskAnalysis: [ + { + ...riskAnalysis, + name: riskAnalysis.name.toUpperCase(), + }, + ], }; await addOneEService(eservice); const riskAnalysisSeed: catalogApi.EServiceRiskAnalysisSeed = { ...buildRiskAnalysisSeed(riskAnalysis), - name: riskAnalysis.name, + name: riskAnalysis.name.toLowerCase(), }; expect( @@ -321,7 +326,7 @@ describe("create risk analysis", () => { logger: genericLogger, }) ).rejects.toThrowError( - riskAnalysisDuplicated(riskAnalysis.name, eservice.id) + riskAnalysisDuplicated(riskAnalysis.name.toLowerCase(), eservice.id) ); }); it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { diff --git a/packages/catalog-process/test/updateDocument.test.ts b/packages/catalog-process/test/updateDocument.test.ts index 5da97514b6..9070d1c5ed 100644 --- a/packages/catalog-process/test/updateDocument.test.ts +++ b/packages/catalog-process/test/updateDocument.test.ts @@ -229,10 +229,10 @@ describe("update Document", () => { eServiceDocumentNotFound(eservice.id, descriptor.id, mockDocument.id) ); }); - it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor", async () => { + it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor, case insensitive", async () => { const document1: Document = { ...getMockDocument(), - prettyName: "test a", + prettyName: "TEST A", }; const document2: Document = { ...getMockDocument(), @@ -254,7 +254,7 @@ describe("update Document", () => { eservice.id, descriptor.id, document2.id, - { prettyName: document1.prettyName }, + { prettyName: document1.prettyName.toLowerCase() }, { authData: getMockAuthData(eservice.producerId), correlationId: generateId(), @@ -263,7 +263,7 @@ describe("update Document", () => { } ) ).rejects.toThrowError( - prettyNameDuplicate(document1.prettyName, descriptor.id) + prettyNameDuplicate(document1.prettyName.toLowerCase(), descriptor.id) ); }); }); diff --git a/packages/catalog-process/test/updateEservice.test.ts b/packages/catalog-process/test/updateEservice.test.ts index afe926ff53..bf454c4542 100644 --- a/packages/catalog-process/test/updateEservice.test.ts +++ b/packages/catalog-process/test/updateEservice.test.ts @@ -345,7 +345,7 @@ describe("update eService", () => { ).rejects.toThrowError(operationForbidden); }); - it("should throw eServiceDuplicate if the updated name is already in use", async () => { + it("should throw eServiceDuplicate if the updated name is already in use, case insensitive", async () => { const eservice1: EService = { ...mockEService, id: generateId(), @@ -364,7 +364,7 @@ describe("update eService", () => { catalogService.updateEService( eservice1.id, { - name: "eservice name already in use", + name: "ESERVICE NAME ALREADY IN USE", description: "eservice description", technology: "REST", mode: "DELIVER", @@ -376,7 +376,7 @@ describe("update eService", () => { logger: genericLogger, } ) - ).rejects.toThrowError(eServiceDuplicate("eservice name already in use")); + ).rejects.toThrowError(eServiceDuplicate("ESERVICE NAME ALREADY IN USE")); }); it("should throw eserviceNotInDraftState if the eservice descriptor is in published state", async () => { diff --git a/packages/catalog-process/test/updateRiskAnalysis.test.ts b/packages/catalog-process/test/updateRiskAnalysis.test.ts index df5d55ee22..fa00e2386b 100644 --- a/packages/catalog-process/test/updateRiskAnalysis.test.ts +++ b/packages/catalog-process/test/updateRiskAnalysis.test.ts @@ -359,7 +359,7 @@ describe("update risk analysis", () => { eServiceRiskAnalysisNotFound(eservice.id, riskAnalysisId) ); }); - it("should throw riskAnalysisDuplicated if risk analysis name is duplicated", async () => { + it("should throw riskAnalysisDuplicated if risk analysis name is duplicated, case insensitive", async () => { const producerTenantKind: TenantKind = randomArrayItem( Object.values(tenantKind) ); @@ -381,7 +381,13 @@ describe("update risk analysis", () => { state: descriptorState.draft, }, ], - riskAnalysis: [riskAnalysis_1, riskAnalysis_2], + riskAnalysis: [ + riskAnalysis_1, + { + ...riskAnalysis_2, + name: riskAnalysis_2.name.toUpperCase(), + }, + ], }; await addOneTenant(producer); @@ -389,7 +395,7 @@ describe("update risk analysis", () => { const riskAnalysisSeed: catalogApi.EServiceRiskAnalysisSeed = { ...buildRiskAnalysisSeed(riskAnalysis_1), - name: riskAnalysis_2.name, + name: riskAnalysis_2.name.toLowerCase(), }; expect( catalogService.updateRiskAnalysis( @@ -404,7 +410,7 @@ describe("update risk analysis", () => { } ) ).rejects.toThrowError( - riskAnalysisDuplicated(riskAnalysis_2.name, eservice.id) + riskAnalysisDuplicated(riskAnalysis_2.name.toLowerCase(), eservice.id) ); }); it("should throw riskAnalysisValidationFailed if the risk analysis is not valid", async () => { diff --git a/packages/catalog-process/test/uploadDocument.test.ts b/packages/catalog-process/test/uploadDocument.test.ts index 1ece2a4fa3..34c6ae6cd9 100644 --- a/packages/catalog-process/test/uploadDocument.test.ts +++ b/packages/catalog-process/test/uploadDocument.test.ts @@ -220,10 +220,10 @@ describe("upload Document", () => { ) ).rejects.toThrowError(interfaceAlreadyExists(descriptor.id)); }); - it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor", async () => { + it("should throw prettyNameDuplicate if a document with the same prettyName already exists in that descriptor, case insensitive", async () => { const document: Document = { ...getMockDocument(), - prettyName: "test", + prettyName: "TEST", }; const descriptor: Descriptor = { ...mockDescriptor, @@ -242,7 +242,7 @@ describe("upload Document", () => { descriptor.id, { ...buildDocumentSeed(), - prettyName: document.prettyName, + prettyName: document.prettyName.toLowerCase(), }, { authData: getMockAuthData(eservice.producerId), @@ -252,7 +252,7 @@ describe("upload Document", () => { } ) ).rejects.toThrowError( - prettyNameDuplicate(document.prettyName, descriptor.id) + prettyNameDuplicate(document.prettyName.toLowerCase(), descriptor.id) ); }); }); From 955993ee3aabcd57d68dbe63494e3d682f8477aa Mon Sep 17 00:00:00 2001 From: Simone Camito <32327779+MalpenZibo@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:27:10 +0100 Subject: [PATCH 023/126] Turbo fixes (#1146) --- docker/docker-compose.yml | 3 +- package.json | 2 +- .../catalog/catalogItemEventNotification.ts | 2 +- .../catalogItemEventNotificationMappers.ts | 2 +- .../src/{gen => protobuf-models}/v1/events.ts | 0 pnpm-lock.yaml | 58 ++++++++-------- turbo.json | 69 +++++-------------- 7 files changed, 49 insertions(+), 87 deletions(-) rename packages/notifier-seeder/src/{gen => protobuf-models}/v1/events.ts (100%) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7f338ba38e..1d08bc9c6e 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -3,7 +3,6 @@ version: "3" name: pagopa-interop services: - # Event Store Postgres DB event-store: volumes: @@ -79,7 +78,7 @@ services: - dynamodb-local restart: always ports: - - "8001:8001" + - "8002:8002" environment: - DYNAMO_ENDPOINT=http://dynamodb-local:8000 - AWS_REGION=eu-south-1 diff --git a/package.json b/package.json index 785be17b2b..e9fe706e5a 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "devDependencies": { "@tsconfig/strictest": "2.0.5", "@tsconfig/node-lts": "20.1.3", - "turbo": "2.0.4" + "turbo": "2.2.3" }, "config": { "protocVersion": "26.1" diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotification.ts index b5aaf4717f..2f2d626f4f 100644 --- a/packages/notifier-seeder/src/models/catalog/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 "../../protobuf-models/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/catalog/catalogItemEventNotificationMappers.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts index 606bbab076..0e5ab3a584 100644 --- a/packages/notifier-seeder/src/models/catalog/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 "../../protobuf-models/v1/events.js"; import { CatalogDescriptorV1Notification, CatalogDocumentV1Notification, diff --git a/packages/notifier-seeder/src/gen/v1/events.ts b/packages/notifier-seeder/src/protobuf-models/v1/events.ts similarity index 100% rename from packages/notifier-seeder/src/gen/v1/events.ts rename to packages/notifier-seeder/src/protobuf-models/v1/events.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index daedbfc6b6..3fb2443e3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: 2.0.5 version: 2.0.5 turbo: - specifier: 2.0.4 - version: 2.0.4 + specifier: 2.2.3 + version: 2.2.3 packages/agreement-email-sender: dependencies: @@ -7292,38 +7292,38 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@2.0.4: - resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} + turbo-darwin-64@2.2.3: + resolution: {integrity: sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.0.4: - resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} + turbo-darwin-arm64@2.2.3: + resolution: {integrity: sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.0.4: - resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} + turbo-linux-64@2.2.3: + resolution: {integrity: sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.0.4: - resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} + turbo-linux-arm64@2.2.3: + resolution: {integrity: sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ==} cpu: [arm64] os: [linux] - turbo-windows-64@2.0.4: - resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} + turbo-windows-64@2.2.3: + resolution: {integrity: sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.0.4: - resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} + turbo-windows-arm64@2.2.3: + resolution: {integrity: sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw==} cpu: [arm64] os: [win32] - turbo@2.0.4: - resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} + turbo@2.2.3: + resolution: {integrity: sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ==} hasBin: true tweetnacl@0.14.5: @@ -14526,32 +14526,32 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@2.0.4: + turbo-darwin-64@2.2.3: optional: true - turbo-darwin-arm64@2.0.4: + turbo-darwin-arm64@2.2.3: optional: true - turbo-linux-64@2.0.4: + turbo-linux-64@2.2.3: optional: true - turbo-linux-arm64@2.0.4: + turbo-linux-arm64@2.2.3: optional: true - turbo-windows-64@2.0.4: + turbo-windows-64@2.2.3: optional: true - turbo-windows-arm64@2.0.4: + turbo-windows-arm64@2.2.3: optional: true - turbo@2.0.4: + turbo@2.2.3: optionalDependencies: - turbo-darwin-64: 2.0.4 - turbo-darwin-arm64: 2.0.4 - turbo-linux-64: 2.0.4 - turbo-linux-arm64: 2.0.4 - turbo-windows-64: 2.0.4 - turbo-windows-arm64: 2.0.4 + turbo-darwin-64: 2.2.3 + turbo-darwin-arm64: 2.2.3 + turbo-linux-64: 2.2.3 + turbo-linux-arm64: 2.2.3 + turbo-windows-64: 2.2.3 + turbo-windows-arm64: 2.2.3 tweetnacl@0.14.5: {} diff --git a/turbo.json b/turbo.json index da5a0c7bff..a3a38b7133 100644 --- a/turbo.json +++ b/turbo.json @@ -1,20 +1,14 @@ { "$schema": "https://turbo.build/schema.json", "ui": "stream", - "globalDependencies": [ - "tsconfig.json" - ], + "daemon": false, + "globalDependencies": ["tsconfig.json"], "globalEnv": ["CI"], "tasks": { "start": { "persistent": true, "cache": false, - "dependsOn": [ - "^build", - "generate-model", - "generate-protobuf", - "//#infra:start" - ] + "dependsOn": ["^build", "//#infra:start"] }, "//#infra:start": { "cache": false @@ -26,62 +20,31 @@ "cache": false }, "build": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["generate-model", "generate-protobuf", "^build"], + "outputs": ["dist"] }, "check": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["generate-model", "generate-protobuf", "^build", "^check"], + "outputs": ["dist"] }, "generate-model": { - "dependsOn": [ - "^generate-model" - ] + "dependsOn": ["^generate-model"], + "outputs": ["src/generated"] }, "generate-protobuf": { - "dependsOn": [ - "^generate-protobuf" - ], - "outputs": [ - "src/gen" - ] + "dependsOn": ["^generate-protobuf"], + "outputs": ["src/gen"] }, "test": { - "dependsOn": [ - "build" - ] + "dependsOn": ["build"] }, "lint": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["build", "^lint"], + "outputs": ["dist"] }, "lint:autofix": { - "dependsOn": [ - "generate-model", - "generate-protobuf", - "^build" - ], - "outputs": [ - "dist" - ] + "dependsOn": ["build"], + "outputs": ["dist"] }, "format:check": {}, "format:write": {} From 3542cdf50406535581040a0fee93003856883606 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Tue, 12 Nov 2024 10:22:32 +0100 Subject: [PATCH 024/126] Feature: Delegation process (#1173) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vittorio Caprio Co-authored-by: Sandro Tajè Co-authored-by: AsterITA Co-authored-by: Eric Camellini --- .../bff/delegation/Approve Delegation.bru | 20 + .../bff/delegation/Create Delegation.bru | 23 + .../bff/delegation/Get Delegation ById.bru | 20 + .../bff/delegation/Get Delegations.bru | 26 + .../bff/delegation/Reject Delegation.bru | 26 + .../bff/delegation/Revoke Delegation.bru | 20 + .../health/Health status endpoint.bru | 11 + .../producer/Approves a delegation.bru | 20 + .../producer/Delegation Creation.bru | 23 + .../producer/List producer delegations.bru | 26 + .../producer/Rejects a delegation.bru | 26 + .../producer/Retrieves a delegation.bru | 20 + .../producer/Revoke a delegation.bru | 16 + collections/environments/PagoPA local.bru | 1 + docker/event-store-init.sql | 19 + package.json | 2 + .../agreement-outbound-writer/package.json | 2 +- .../src/service/processor.ts | 4 +- packages/api-clients/open-api/bffApi.yml | 1133 +++++++++++++++-- .../api-clients/open-api/delegationApi.yml | 567 +++++++++ packages/api-clients/open-api/tenantApi.yml | 47 +- packages/api-clients/src/index.ts | 1 + packages/api-clients/template-bff.hbs | 2 + packages/api-clients/template.hbs | 2 + .../src/services/attributeRegistryService.ts | 6 +- ...tributeRegistryService.integration.test.ts | 10 +- packages/backend-for-frontend/.env | 1 + .../src/api/delegationApiConverter.ts | 108 ++ packages/backend-for-frontend/src/app.ts | 6 +- .../src/clients/clientsProvider.ts | 15 + .../backend-for-frontend/src/config/config.ts | 12 + .../backend-for-frontend/src/model/errors.ts | 23 +- .../src/routers/catalogRouter.ts | 2 + .../src/routers/delegationRouter.ts | 104 ++ .../src/routers/producerDelegationRouter.ts | 128 ++ .../src/routers/tenantRouter.ts | 20 +- .../src/services/catalogService.ts | 13 +- .../src/services/delegationService.ts | 287 +++++ .../src/services/tenantService.ts | 10 + .../src/services/validators.ts | 42 +- .../src/utilities/errorMappers.ts | 8 + packages/catalog-outbound-writer/package.json | 2 +- .../commons-test/src/eventStoreTestUtils.ts | 33 +- .../src/setupTestContainersVitest.ts | 4 + packages/commons-test/src/testUtils.ts | 85 ++ .../commons/src/config/kafkaTopicConfig.ts | 10 + .../commons/src/pdf-generator/pdfGenerator.ts | 4 +- .../src/repositories/ReadModelRepository.ts | 10 +- .../compute-agreements-consumer/src/index.ts | 3 +- packages/delegation-process/.env | 30 + packages/delegation-process/Dockerfile | 45 + packages/delegation-process/aws.config.local | 4 + packages/delegation-process/package.json | 52 + packages/delegation-process/src/app.ts | 31 + .../delegation-process/src/config/config.ts | 27 + packages/delegation-process/src/index.ts | 7 + .../src/model/domain/apiConverter.ts | 145 +++ .../src/model/domain/errors.ts | 144 +++ .../src/model/domain/models.ts | 14 + .../src/model/domain/toEvent.ts | 81 ++ .../resources/assets/font/Montserrat-Bold.ttf | Bin 0 -> 198612 bytes .../assets/font/Montserrat-Italic.ttf | Bin 0 -> 202912 bytes .../assets/font/Montserrat-Medium.ttf | Bin 0 -> 198616 bytes .../assets/font/Montserrat-Regular.ttf | Bin 0 -> 198552 bytes .../assets/font/Montserrat-SemiBold.ttf | Bin 0 -> 198720 bytes .../resources/assets/media/logo_pagopa.png | Bin 0 -> 4514 bytes .../assets/media/pictogram_pagopa.svg | 4 + .../templates/delegationApprovedTemplate.html | 87 ++ .../templates/delegationRevokedTemplate.html | 80 ++ .../templates/delegationTemplate.css | 130 ++ .../src/routers/DelegationProducerRouter.ts | 179 +++ .../src/routers/DelegationRouter.ts | 137 ++ .../src/routers/HealthRouter.ts | 8 + .../src/services/delegationContractBuilder.ts | 185 +++ .../src/services/delegationProducerService.ts | 280 ++++ .../src/services/delegationService.ts | 63 + .../src/services/readModelService.ts | 251 ++++ .../src/services/validators.ts | 144 +++ .../src/utilites/errorMappers.ts | 62 + .../delegation-process/test/.eslintrc.json | 7 + .../test/approveProducerDelegation.test.ts | 247 ++++ .../test/createProducerDelegation.test.ts | 564 ++++++++ .../test/getDelegationById.test.ts | 33 + .../test/getDelegations.test.ts | 68 + .../test/rejectProducerDelegation.test.ts | 127 ++ .../test/revokeProducerDelegation.test.ts | 371 ++++++ .../delegation-process/test/tsconfig.json | 4 + packages/delegation-process/test/utils.ts | 137 ++ .../test/vitestGlobalSetup.ts | 3 + .../delegation-process/tsconfig.check.json | 7 + packages/delegation-process/tsconfig.json | 7 + packages/delegation-process/vitest.config.ts | 11 + packages/delegation-readmodel-writer/.env | 13 + .../delegation-readmodel-writer/Dockerfile | 45 + .../delegation-readmodel-writer/package.json | 42 + .../src/config/config.ts | 16 + .../src/delegationConsumerServiceV2.ts | 38 + .../delegation-readmodel-writer/src/index.ts | 45 + .../test/consumerServiceV2.test.ts | 121 ++ .../test/tsconfig.json | 4 + .../delegation-readmodel-writer/test/utils.ts | 10 + .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 + .../delegation-readmodel-writer/tsconfig.json | 9 + .../vitest.config.ts | 11 + .../proto/v2/delegation/delegation.proto | 54 + .../models/proto/v2/delegation/events.proto | 21 + packages/models/proto/v2/tenant/events.proto | 4 + packages/models/proto/v2/tenant/tenant.proto | 5 + packages/models/src/brandedIds.ts | 10 + packages/models/src/constants.ts | 4 + packages/models/src/delegation/delegation.ts | 75 ++ .../models/src/delegation/delegationEvents.ts | 85 ++ .../src/delegation/protobufConverterFromV2.ts | 125 ++ .../src/delegation/protobufConverterToV2.ts | 87 ++ packages/models/src/index.ts | 9 + .../src/read-models/delegationReadModel.ts | 5 + .../src/tenant/protobufConverterFromV2.ts | 16 +- .../src/tenant/protobufConverterToV2.ts | 8 + packages/models/src/tenant/tenant.ts | 14 +- packages/models/src/tenant/tenantEvents.ts | 9 + packages/purpose-outbound-writer/package.json | 2 +- packages/tenant-outbound-writer/package.json | 2 +- .../src/converters/toOutboundEventV2.ts | 1 + .../src/model/domain/apiConverter.ts | 5 + .../tenant-process/src/model/domain/errors.ts | 11 + .../src/model/domain/toEvent.ts | 17 + .../src/routers/TenantRouter.ts | 25 + .../src/services/tenantService.ts | 52 +- .../tenant-process/src/services/validators.ts | 27 +- .../src/utilities/errorMappers.ts | 10 + .../test/addCertifiedAttribute.test.ts | 7 +- ...signTenantDelegatedProducerFeature.test.ts | 107 ++ .../test/revokeCertifiedAttributeById.test.ts | 3 +- .../test/selfcareUpsertTenant.test.ts | 6 +- .../src/tenantConsumerServiceV2.ts | 1 + .../test/converterV1.ts | 4 +- pnpm-lock.yaml | 287 ++++- 138 files changed, 8029 insertions(+), 212 deletions(-) create mode 100644 collections/bff/delegation/Approve Delegation.bru create mode 100644 collections/bff/delegation/Create Delegation.bru create mode 100644 collections/bff/delegation/Get Delegation ById.bru create mode 100644 collections/bff/delegation/Get Delegations.bru create mode 100644 collections/bff/delegation/Reject Delegation.bru create mode 100644 collections/bff/delegation/Revoke Delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru create mode 100644 collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru create mode 100644 packages/api-clients/open-api/delegationApi.yml create mode 100644 packages/backend-for-frontend/src/api/delegationApiConverter.ts create mode 100644 packages/backend-for-frontend/src/routers/delegationRouter.ts create mode 100644 packages/backend-for-frontend/src/routers/producerDelegationRouter.ts create mode 100644 packages/backend-for-frontend/src/services/delegationService.ts create mode 100644 packages/delegation-process/.env create mode 100644 packages/delegation-process/Dockerfile create mode 100644 packages/delegation-process/aws.config.local create mode 100644 packages/delegation-process/package.json create mode 100644 packages/delegation-process/src/app.ts create mode 100644 packages/delegation-process/src/config/config.ts create mode 100644 packages/delegation-process/src/index.ts create mode 100644 packages/delegation-process/src/model/domain/apiConverter.ts create mode 100644 packages/delegation-process/src/model/domain/errors.ts create mode 100644 packages/delegation-process/src/model/domain/models.ts create mode 100644 packages/delegation-process/src/model/domain/toEvent.ts create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Bold.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Italic.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Medium.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-Regular.ttf create mode 100644 packages/delegation-process/src/resources/assets/font/Montserrat-SemiBold.ttf create mode 100644 packages/delegation-process/src/resources/assets/media/logo_pagopa.png create mode 100644 packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg create mode 100644 packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html create mode 100644 packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html create mode 100644 packages/delegation-process/src/resources/templates/delegationTemplate.css create mode 100644 packages/delegation-process/src/routers/DelegationProducerRouter.ts create mode 100644 packages/delegation-process/src/routers/DelegationRouter.ts create mode 100644 packages/delegation-process/src/routers/HealthRouter.ts create mode 100644 packages/delegation-process/src/services/delegationContractBuilder.ts create mode 100644 packages/delegation-process/src/services/delegationProducerService.ts create mode 100644 packages/delegation-process/src/services/delegationService.ts create mode 100644 packages/delegation-process/src/services/readModelService.ts create mode 100644 packages/delegation-process/src/services/validators.ts create mode 100644 packages/delegation-process/src/utilites/errorMappers.ts create mode 100644 packages/delegation-process/test/.eslintrc.json create mode 100644 packages/delegation-process/test/approveProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/createProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/getDelegationById.test.ts create mode 100644 packages/delegation-process/test/getDelegations.test.ts create mode 100644 packages/delegation-process/test/rejectProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/revokeProducerDelegation.test.ts create mode 100644 packages/delegation-process/test/tsconfig.json create mode 100644 packages/delegation-process/test/utils.ts create mode 100644 packages/delegation-process/test/vitestGlobalSetup.ts create mode 100644 packages/delegation-process/tsconfig.check.json create mode 100644 packages/delegation-process/tsconfig.json create mode 100644 packages/delegation-process/vitest.config.ts create mode 100644 packages/delegation-readmodel-writer/.env create mode 100644 packages/delegation-readmodel-writer/Dockerfile create mode 100644 packages/delegation-readmodel-writer/package.json create mode 100644 packages/delegation-readmodel-writer/src/config/config.ts create mode 100644 packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts create mode 100644 packages/delegation-readmodel-writer/src/index.ts create mode 100644 packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts create mode 100644 packages/delegation-readmodel-writer/test/tsconfig.json create mode 100644 packages/delegation-readmodel-writer/test/utils.ts create mode 100644 packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts create mode 100644 packages/delegation-readmodel-writer/tsconfig.check.json create mode 100644 packages/delegation-readmodel-writer/tsconfig.json create mode 100644 packages/delegation-readmodel-writer/vitest.config.ts create mode 100644 packages/models/proto/v2/delegation/delegation.proto create mode 100644 packages/models/proto/v2/delegation/events.proto create mode 100644 packages/models/src/constants.ts create mode 100644 packages/models/src/delegation/delegation.ts create mode 100644 packages/models/src/delegation/delegationEvents.ts create mode 100644 packages/models/src/delegation/protobufConverterFromV2.ts create mode 100644 packages/models/src/delegation/protobufConverterToV2.ts create mode 100644 packages/models/src/read-models/delegationReadModel.ts create mode 100644 packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts diff --git a/collections/bff/delegation/Approve Delegation.bru b/collections/bff/delegation/Approve Delegation.bru new file mode 100644 index 0000000000..d82cf26b2c --- /dev/null +++ b/collections/bff/delegation/Approve Delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Approve delegation + type: http + seq: 1 +} + +post { + url: {{host-bff}}/producer/delegations/:delegationId/approve + body: none + auth: none +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Create Delegation.bru b/collections/bff/delegation/Create Delegation.bru new file mode 100644 index 0000000000..b13d48c183 --- /dev/null +++ b/collections/bff/delegation/Create Delegation.bru @@ -0,0 +1,23 @@ +meta { + name: Create new delegation + type: http + seq: 1 +} + +post { + url: {{host-bff}}/producer/delegations + body: json + auth: none +} + +body:json { + { + "eserviceId": "2f19b864-0fdc-4d2d-b01f-52bfad74fd34", + "delegateId": "aada3e71-c544-4fa0-beb8-81274ae0addf" + } +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Get Delegation ById.bru b/collections/bff/delegation/Get Delegation ById.bru new file mode 100644 index 0000000000..3510272d24 --- /dev/null +++ b/collections/bff/delegation/Get Delegation ById.bru @@ -0,0 +1,20 @@ +meta { + name: Retrieves a delegation by ID + type: http + seq: 1 +} + +get { + url: {{host-bff}}/delegations/:delegationId + body: none + auth: none +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Get Delegations.bru b/collections/bff/delegation/Get Delegations.bru new file mode 100644 index 0000000000..d9dc4460ca --- /dev/null +++ b/collections/bff/delegation/Get Delegations.bru @@ -0,0 +1,26 @@ +meta { + name: List delegations + type: http + seq: 1 +} + +get { + url: {{host-bff}}/delegations?offset=0&limit=50 + body: none + auth: none +} + +params:query { + offset: 0 + limit: 50 + ~kind: DELEGATED_PRODUCER + ~states: WAITING_FOR_APPROVAL + ~delegatorIds: + ~delegateIds: + ~eserviceIds: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Reject Delegation.bru b/collections/bff/delegation/Reject Delegation.bru new file mode 100644 index 0000000000..5dcd6cb2fe --- /dev/null +++ b/collections/bff/delegation/Reject Delegation.bru @@ -0,0 +1,26 @@ +meta { + name: Reject delegation + type: http + seq: 1 +} + +post { + url: {{host-bff}}/producer/delegations/:delegationId/reject + body: json + auth: none +} + +body:json { + { + "rejectionReason": "I'm not feel comfortable with this delegation" + } +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/bff/delegation/Revoke Delegation.bru b/collections/bff/delegation/Revoke Delegation.bru new file mode 100644 index 0000000000..1e75ef2e8d --- /dev/null +++ b/collections/bff/delegation/Revoke Delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Revoke a delegation by ID + type: http + seq: 1 +} + +delete { + url: {{host-bff}}/producer/delegations/:delegationId + body: none + auth: none +} + +params:path { + delegationId: dd10132c-f3c9-45cf-994c-3a312bc4ab4e +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru b/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru new file mode 100644 index 0000000000..30508a436a --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru @@ -0,0 +1,11 @@ +meta { + name: Health status endpoint + type: http + seq: 1 +} + +get { + url: {{host-delegation}}/status + body: none + auth: none +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru new file mode 100644 index 0000000000..23bd4e2e56 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Approves a delegation + type: http + seq: 4 +} + +post { + url: {{host-delegation}}/producer/delegations/:delegationId/approve + body: none + auth: none +} + +params:path { + delegationId: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru b/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru new file mode 100644 index 0000000000..fa36a2e7e5 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru @@ -0,0 +1,23 @@ +meta { + name: Delegation Creation + type: http + seq: 3 +} + +post { + url: {{host-delegation}}/producer/delegations + body: json + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} + +body:json { + { + "eserviceId": "", + "delegateId": "" + } +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru b/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru new file mode 100644 index 0000000000..733ec7c274 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru @@ -0,0 +1,26 @@ +meta { + name: List producer delegations + type: http + seq: 1 +} + +get { + url: {{host-delegation}}/delegations?offset=0&limit=50 + body: none + auth: none +} + +params:query { + offset: 0 + limit: 50 + ~kind: DELEGATED_PRODUCER + ~delegationStates: WAITING_FOR_APPROVAL + ~delegatorIds: + ~delegateIds: + ~eserviceIds: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru new file mode 100644 index 0000000000..b2b1477d62 --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru @@ -0,0 +1,26 @@ +meta { + name: Rejects a delegation + type: http + seq: 5 +} + +post { + url: {{host-delegation}}/producer/delegations/:delegationId/reject + body: json + auth: none +} + +params:path { + delegationId: +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} + +body:json { + { + "rejectionReason": "" + } +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru new file mode 100644 index 0000000000..4e9425bd6d --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru @@ -0,0 +1,20 @@ +meta { + name: Retrieves a delegation + type: http + seq: 2 +} + +get { + url: {{host-delegation}}/delegations/:delegationId + body: none + auth: none +} + +params:path { + delegationId: 123e4567-e89b-12d3-a456-426614174000 +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru b/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru new file mode 100644 index 0000000000..69741abb1d --- /dev/null +++ b/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru @@ -0,0 +1,16 @@ +meta { + name: Revoke a delegation + type: http + seq: 6 +} + +delete { + url: {{host-delegation}}/producer/delegations/:delegationId + body: none + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/environments/PagoPA local.bru b/collections/environments/PagoPA local.bru index db3d9de5bb..f0051f88bb 100644 --- a/collections/environments/PagoPA local.bru +++ b/collections/environments/PagoPA local.bru @@ -9,6 +9,7 @@ vars { host-tenant: http://localhost:3500 host-purpose: http://localhost:3400 host-authorization: http://localhost:3300 + host-delegation: http://localhost:3800 host-api-gw: http://localhost:3700/api-gateway/0.0 JWT-M2M: Bearer {{process.env.JWT-M2M}} } diff --git a/docker/event-store-init.sql b/docker/event-store-init.sql index acb43f819b..8b7c90b3c9 100644 --- a/docker/event-store-init.sql +++ b/docker/event-store-init.sql @@ -119,3 +119,22 @@ CREATE TABLE notification_event.producer_keys_events ( kid varchar NOT NULL, event_type varchar NOT NULL ); + +create schema delegation; +CREATE TABLE delegation.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) +); \ No newline at end of file diff --git a/package.json b/package.json index e9fe706e5a..9abb632b87 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "start:ivass-certified-attributes-importer": "turbo start --filter pagopa-interop-ivass-certified-attributes-importer", "start:pn-consumers": "turbo start --filter pagopa-interop-pn-consumers", "start:one-trust-notices": "turbo start --filter pagopa-interop-one-trust-notices", + "start:delegation": "turbo start --filter pagopa-interop-delegation-process", + "start:delegation-readmodel-writer": "turbo start --filter pagopa-interop-delegation-readmodel-writer", "start:datalake-data-export": "turbo start --filter pagopa-interop-datalake-data-export", "test": "turbo test", "build": "turbo build", diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 29c67eb697..c414df4e13 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/anac-certified-attributes-importer/src/service/processor.ts b/packages/anac-certified-attributes-importer/src/service/processor.ts index eb82b0e825..2499b31134 100644 --- a/packages/anac-certified-attributes-importer/src/service/processor.ts +++ b/packages/anac-certified-attributes-importer/src/service/processor.ts @@ -1,7 +1,7 @@ /* eslint-disable max-params */ import { parse } from "csv/sync"; import { Logger, RefreshableInteropToken, zipBy } from "pagopa-interop-commons"; -import { CorrelationId } from "pagopa-interop-models"; +import { TenantFeatureCertifier, CorrelationId } from "pagopa-interop-models"; import { AnacAttributes, AttributeIdentifiers, @@ -201,7 +201,7 @@ async function getAttributesIdentifiers( anacTenantId ); const certifier = anacTenant.features.find( - (f) => f.type === "PersistentCertifier" + (f): f is TenantFeatureCertifier => f.type === "PersistentCertifier" ); if (!certifier) { diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index dcce9750ca..2c9ab3e0e7 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -51,6 +51,16 @@ tags: externalDocs: description: Find out more url: "http://swagger.io" + - name: delegations + description: Delegation Module + externalDocs: + description: Find out more + url: "http://swagger.io" + - name: producerDelegations + description: Producer Delegation Module + externalDocs: + description: Find out more + url: "http://swagger.io" - name: tools description: Utility Module externalDocs: @@ -8312,6 +8322,66 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /tenants/delegatedProducer: + post: + tags: + - tenants + summary: Assign delegated producer feature to tenant caller + operationId: assignTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature assigned + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "409": + description: Feature already assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available /clients: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" @@ -9442,7 +9512,7 @@ paths: minItems: 1 items: type: string - format: uuid + format: uuid responses: "200": description: Users added @@ -11005,7 +11075,7 @@ paths: minItems: 1 items: type: string - format: uuid + format: uuid responses: "204": description: Users added @@ -11955,143 +12025,780 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available - "/status": + /delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" get: - security: [] tags: - - health - summary: Health status endpoint - description: Return ok - operationId: getStatus + - delegations + summary: List delegations + description: List delegations + operationId: getDelegations + parameters: + - 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 + - in: query + name: states + description: comma separated sequence of delegation states to filter the results with + schema: + type: array + items: + $ref: "#/components/schemas/DelegationState" + default: [] + explode: false + - in: query + name: delegatorIds + schema: + type: array + items: + type: string + format: uuid + description: "The delegator ids to filter by" + - in: query + name: delegateIds + schema: + type: array + items: + type: string + format: uuid + description: "The delegated ids to filter by" + - in: query + name: kind + schema: + $ref: "#/components/schemas/DelegationKind" + description: "The delegation kind to filter by" + - in: query + name: eserviceIds + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false responses: "200": - description: successful operation + description: Delegations found + content: + application/json: + schema: + $ref: "#/components/schemas/CompactDelegations" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request content: application/json: schema: $ref: "#/components/schemas/Problem" -components: - parameters: - CorrelationIdHeader: - in: header - name: X-Correlation-Id - required: true - schema: - type: string - schemas: - RejectPurposeVersionPayload: - type: object - additionalProperties: false - description: models the reject payload for this purpose version. - properties: - rejectionReason: - type: string - required: - - rejectionReason - GoogleSAMLPayload: - type: object - additionalProperties: false - properties: - SAMLResponse: - type: string - description: SAML response - RelayState: - type: string - nullable: true - required: - - SAMLResponse - SAMLTokenRequest: - type: object - additionalProperties: false - properties: - saml2: - type: string - description: SAML - tenantId: - type: string - format: uuid - description: tenant id - required: - - saml2 - - tenantId - AccessTokenRequest: - type: object - additionalProperties: false - required: - - client_assertion - - client_assertion_type - - grant_type - properties: - client_id: - type: string - example: e58035ce-c753-4f72-b613-46f8a17b71cc - client_assertion: - type: string - format: jws - client_assertion_type: - type: string - grant_type: - type: string - PrivacyNotice: - type: object - additionalProperties: false - properties: - id: - type: string - format: uuid - userId: - type: string - format: uuid - consentType: - $ref: "#/components/schemas/ConsentType" - firstAccept: - type: boolean - isUpdated: - type: boolean - latestVersionId: - type: string - format: uuid - required: - - id - - userId - - consentType - - firstAccept - - isUpdated - - latestVersionId - ConsentType: - type: string - description: Consent Type - enum: - - PP - - TOS - PrivacyNoticeSeed: - type: object - additionalProperties: false - properties: - latestVersionId: - type: string - format: uuid - required: - - latestVersionId - RiskAnalysisFormConfig: - type: object - additionalProperties: false - properties: - version: - type: string - questions: - type: array - items: - $ref: "#/components/schemas/FormConfigQuestion" - required: - - version - - questions - FormConfigQuestion: - type: object - additionalProperties: false - properties: - id: - type: string + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /producer/delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + summary: Producer delegation creation + operationId: createProducerDelegation + responses: + "200": + description: Delegation created + content: + application/json: + schema: + $ref: "#/components/schemas/CreatedResource" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + tags: + - producerDelegations + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DelegationSeed" + description: payload for producer delegation creation + required: true + description: creates the producer delegation + "/producer/delegations/{delegationId}/approve": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - producerDelegations + summary: Approves a producer delegation + description: Approves a producer delegation + operationId: approveDelegation + parameters: + - name: delegationId + in: path + description: The identifier of the delegation + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation approved + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "/producer/delegations/{delegationId}/reject": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + tags: + - producerDelegations + summary: Rejects a producer delegation + description: Rejects a producer delegation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RejectDelegationPayload" + operationId: rejectDelegation + parameters: + - name: delegationId + in: path + description: The identifier of the delegation + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation rejected + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /delegations/{delegationId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + get: + description: Retrieves delegation + summary: Retrieves delegation + tags: + - delegations + operationId: getDelegation + responses: + "200": + description: Producer delegation retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Delegation" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /producer/delegations/{delegationId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + delete: + description: Revokes a producer delegation + tags: + - producerDelegations + summary: Revokes a producer delegation + operationId: revokeProducerDelegation + responses: + "204": + description: Delegation revoked + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "/status": + get: + security: [] + tags: + - health + summary: Health status endpoint + description: Return ok + operationId: getStatus + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" +components: + parameters: + CorrelationIdHeader: + in: header + name: X-Correlation-Id + required: true + schema: + type: string + schemas: + RejectPurposeVersionPayload: + type: object + additionalProperties: false + description: models the reject payload for this purpose version. + properties: + rejectionReason: + type: string + required: + - rejectionReason + GoogleSAMLPayload: + type: object + additionalProperties: false + properties: + SAMLResponse: + type: string + description: SAML response + RelayState: + type: string + nullable: true + required: + - SAMLResponse + SAMLTokenRequest: + type: object + additionalProperties: false + properties: + saml2: + type: string + description: SAML + tenantId: + type: string + format: uuid + description: tenant id + required: + - saml2 + - tenantId + AccessTokenRequest: + type: object + additionalProperties: false + required: + - client_assertion + - client_assertion_type + - grant_type + properties: + client_id: + type: string + example: e58035ce-c753-4f72-b613-46f8a17b71cc + client_assertion: + type: string + format: jws + client_assertion_type: + type: string + grant_type: + type: string + PrivacyNotice: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + userId: + type: string + format: uuid + consentType: + $ref: "#/components/schemas/ConsentType" + firstAccept: + type: boolean + isUpdated: + type: boolean + latestVersionId: + type: string + format: uuid + required: + - id + - userId + - consentType + - firstAccept + - isUpdated + - latestVersionId + ConsentType: + type: string + description: Consent Type + enum: + - PP + - TOS + PrivacyNoticeSeed: + type: object + additionalProperties: false + properties: + latestVersionId: + type: string + format: uuid + required: + - latestVersionId + RiskAnalysisFormConfig: + type: object + additionalProperties: false + properties: + version: + type: string + questions: + type: array + items: + $ref: "#/components/schemas/FormConfigQuestion" + required: + - version + - questions + FormConfigQuestion: + type: object + additionalProperties: false + properties: + id: + type: string label: $ref: "#/components/schemas/LocalizedText" infoLabel: @@ -14093,11 +14800,17 @@ components: - results - pagination TenantFeature: - type: object - additionalProperties: false - properties: - certifier: - $ref: "#/components/schemas/Certifier" + oneOf: + - type: object + additionalProperties: false + properties: + certifier: + $ref: "#/components/schemas/Certifier" + - type: object + additionalProperties: false + properties: + delegatedProducer: + $ref: "#/components/schemas/DelegatedProducer" Certifier: description: Certifier Tenant Feature type: object @@ -14107,6 +14820,16 @@ components: type: string required: - certifierId + DelegatedProducer: + description: Delegated producer Tenant Feature + type: object + additionalProperties: false + properties: + availabilityTimestamp: + type: string + format: date-time + required: + - availabilityTimestamp CompactTenant: properties: id: @@ -14463,6 +15186,144 @@ components: format: uuid required: - id + DelegationKind: + type: string + description: Delegation State + enum: + - DELEGATED_PRODUCER + - DELEGATED_CONSUMER + DelegationState: + type: string + description: Delegation State + enum: + - WAITING_FOR_APPROVAL + - ACTIVE + - REJECTED + - REVOKED + DelegationTenant: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + required: + - id + - name + DelegationEService: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + description: + type: string + producerId: + type: string + format: uuid + producerName: + type: string + required: + - id + - name + - producerId + - producerName + Delegation: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + eservice: + $ref: "#/components/schemas/DelegationEService" + delegate: + $ref: "#/components/schemas/DelegationTenant" + delegator: + $ref: "#/components/schemas/DelegationTenant" + activationContract: + $ref: "#/components/schemas/Document" + revocationContract: + $ref: "#/components/schemas/Document" + submittedAt: + type: string + format: date-time + rejectionReason: + type: string + state: + $ref: "#/components/schemas/DelegationState" + kind: + $ref: "#/components/schemas/DelegationKind" + required: + - id + - eservice + - delegate + - delegator + - state + - kind + CompactDelegation: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + eserviceName: + type: string + delegate: + $ref: "#/components/schemas/DelegationTenant" + delegator: + $ref: "#/components/schemas/DelegationTenant" + state: + $ref: "#/components/schemas/DelegationState" + kind: + $ref: "#/components/schemas/DelegationKind" + required: + - id + - eserviceName + - delegate + - delegator + - state + - kind + CompactDelegations: + type: object + additionalProperties: false + properties: + results: + type: array + items: + $ref: "#/components/schemas/CompactDelegation" + pagination: + $ref: "#/components/schemas/Pagination" + required: + - results + - pagination + DelegationSeed: + type: object + additionalProperties: false + properties: + eserviceId: + type: string + format: uuid + delegateId: + type: string + format: uuid + required: + - eserviceId + - delegateId + RejectDelegationPayload: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + required: + - rejectionReason Problem: type: object additionalProperties: false diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml new file mode 100644 index 0000000000..c87eeff3b2 --- /dev/null +++ b/packages/api-clients/open-api/delegationApi.yml @@ -0,0 +1,567 @@ +openapi: 3.0.3 +info: + title: Delegation Process Micro Service + description: This service is the delegation process + version: "v1" + 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: "/delegation-process/v1" + description: This service is the Delegation Process +security: + - bearerAuth: [] +tags: + - name: delegation + description: Delegation common operations + externalDocs: + description: Find out more + url: http://swagger.io + - name: producer + description: Lead organization + 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: + /delegations: + get: + description: List delegations + summary: List delegations + tags: + - delegation + operationId: getDelegations + parameters: + - in: query + name: delegationStates + required: false + schema: + type: array + items: + $ref: "#/components/schemas/DelegationState" + default: [] + explode: false + - in: query + name: delegatorIds + required: false + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: delegateIds + required: false + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + - in: query + name: kind + required: false + schema: + $ref: "#/components/schemas/DelegationKind" + - 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 + - in: query + name: eserviceIds + schema: + type: array + items: + type: string + format: uuid + default: [] + explode: false + responses: + "200": + description: Delegations retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Delegations" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /delegations/{delegationId}: + get: + description: Retrieves a delegation + summary: Retrieves a delegation + tags: + - delegation + operationId: getDelegation + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + responses: + "200": + description: Delegation retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/Delegation" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations: + post: + description: creates the delegation + summary: Delegation Creation + tags: + - producer + operationId: createProducerDelegation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/DelegationSeed" + description: payload for delegation creation + required: true + responses: + "200": + description: Delegation created. + content: + application/json: + schema: + $ref: "#/components/schemas/Delegation" + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations/{delegationId}/approve: + post: + description: Approves a delegation + summary: Approves a delegation + tags: + - producer + operationId: approveProducerDelegation + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation approved + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations/{delegationId}/reject: + post: + description: Rejects a delegation + summary: Rejects a delegation + tags: + - producer + operationId: rejectProducerDelegation + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/RejectDelegationPayload" + required: true + description: payload for delegation rejection + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: Delegation rejected + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /producer/delegations/{delegationId}: + parameters: + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + delete: + description: Revokes a delegation + summary: Revokes a delegation + tags: + - producer + operationId: revokeProducerDelegation + responses: + "204": + description: Delegation revoked + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /status: + get: + description: Returns ok + summary: Health status endpoint + tags: + - health + operationId: getStatus + security: [] + responses: + "200": + description: successful operation + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" +components: + schemas: + Delegations: + type: object + additionalProperties: false + properties: + results: + type: array + items: + $ref: "#/components/schemas/Delegation" + totalCount: + type: integer + format: int32 + required: + - results + - totalCount + CreatedResource: + type: object + additionalProperties: false + description: contains the id of the created resource + properties: + id: + type: string + format: uuid + required: + - id + Delegation: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + delegatorId: + type: string + format: uuid + delegateId: + type: string + format: uuid + eserviceId: + type: string + format: uuid + createdAt: + type: string + format: date-time + submittedAt: + type: string + format: date-time + approvedAt: + type: string + format: date-time + rejectedAt: + type: string + format: date-time + rejectionReason: + type: string + revokedAt: + type: string + format: date-time + state: + $ref: "#/components/schemas/DelegationState" + kind: + $ref: "#/components/schemas/DelegationKind" + activationContract: + $ref: "#/components/schemas/DelegationContractDocument" + revocationContract: + $ref: "#/components/schemas/DelegationContractDocument" + stamps: + $ref: "#/components/schemas/DelegationStamps" + required: + - id + - delegatorId + - delegateId + - eserviceId + - createdAt + - submittedAt + - state + - kind + - stamps + DelegationState: + type: string + enum: + - WAITING_FOR_APPROVAL + - ACTIVE + - REJECTED + - REVOKED + DelegationKind: + type: string + enum: + - DELEGATED_PRODUCER + - DELEGATED_CONSUMER + DelegationContractDocument: + type: object + additionalProperties: false + properties: + id: + type: string + format: uuid + name: + type: string + prettyName: + type: string + contentType: + type: string + path: + type: string + createdAt: + type: string + format: date-time + required: + - id + - name + - prettyName + - contentType + - path + - createdAt + DelegationStamp: + type: object + additionalProperties: false + properties: + who: + type: string + format: uuid + when: + type: string + format: date-time + required: + - who + - when + DelegationStamps: + type: object + additionalProperties: false + properties: + submission: + $ref: "#/components/schemas/DelegationStamp" + activation: + $ref: "#/components/schemas/DelegationStamp" + rejection: + $ref: "#/components/schemas/DelegationStamp" + revocation: + $ref: "#/components/schemas/DelegationStamp" + required: + - submission + DelegationSeed: + type: object + additionalProperties: false + properties: + eserviceId: + type: string + format: uuid + delegateId: + type: string + format: uuid + required: + - eserviceId + - delegateId + RejectDelegationPayload: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + required: + - rejectionReason + 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/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index d4dc2b3864..4bed5af187 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -975,6 +975,27 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + /tenants/delegatedProducer: + post: + tags: + - tenant + summary: Assign delegated producer feature to tenant caller + operationId: assignTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature assigned + "409": + description: Feature already assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /status: get: security: [] @@ -1119,11 +1140,17 @@ components: - results - totalCount TenantFeature: - type: object - additionalProperties: false - properties: - certifier: - $ref: "#/components/schemas/Certifier" + oneOf: + - type: object + additionalProperties: false + properties: + certifier: + $ref: "#/components/schemas/Certifier" + - type: object + additionalProperties: false + properties: + delegatedProducer: + $ref: "#/components/schemas/DelegatedProducer" Certifier: description: Certifier Tenant Feature type: object @@ -1133,6 +1160,16 @@ components: type: string required: - certifierId + DelegatedProducer: + description: Delegated producer Tenant Feature + type: object + additionalProperties: false + properties: + availabilityTimestamp: + type: string + format: date-time + required: + - availabilityTimestamp CertifiedAttribute: type: object additionalProperties: false diff --git a/packages/api-clients/src/index.ts b/packages/api-clients/src/index.ts index ed8c86e0f6..2a5174bbba 100644 --- a/packages/api-clients/src/index.ts +++ b/packages/api-clients/src/index.ts @@ -9,4 +9,5 @@ export * as selfcareV2ClientApi from "./generated/selfcareV2ClientApi.js"; export * as tenantApi from "./generated/tenantApi.js"; export * as apiGatewayApi from "./apiGatewayApi.js"; export * as notifierApi from "./generated/notifierApi.js"; +export * as delegationApi from "./generated/delegationApi.js"; export * from "./selfcareClients.js"; diff --git a/packages/api-clients/template-bff.hbs b/packages/api-clients/template-bff.hbs index c53bf62143..a194e18ce6 100644 --- a/packages/api-clients/template-bff.hbs +++ b/packages/api-clients/template-bff.hbs @@ -48,6 +48,8 @@ export const {{@key}}Endpoints = makeApi([ schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(EServiceDescriptorState).optional().default([])), z.array(EServiceDescriptorState).optional().default([])]) {{else if (and (eq type "Query") (eq schema "z.array(PurposeVersionState).optional().default([])"))}} schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(PurposeVersionState).optional().default([])), z.array(PurposeVersionState).optional().default([])]) + {{else if (and (eq type "Query") (eq schema "z.array(DelegationState).optional().default([])"))}} + schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(DelegationState).optional().default([])), z.array(DelegationState).optional().default([])]) {{else}} schema: {{{schema}}}, {{/if}} diff --git a/packages/api-clients/template.hbs b/packages/api-clients/template.hbs index 3c21e0ece0..04d8e3669b 100644 --- a/packages/api-clients/template.hbs +++ b/packages/api-clients/template.hbs @@ -48,6 +48,8 @@ export const {{@key}}Endpoints = makeApi([ schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(EServiceDescriptorState).optional().default([])), z.array(EServiceDescriptorState).optional().default([])]) {{else if (and (eq type "Query") (eq schema "z.array(PurposeVersionState).optional().default([])"))}} schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(PurposeVersionState).optional().default([])), z.array(PurposeVersionState).optional().default([])]) + {{else if (and (eq type "Query") (eq schema "z.array(DelegationState).optional().default([])"))}} + schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(DelegationState).optional().default([])), z.array(DelegationState).optional().default([])]) {{else}} schema: {{{schema}}}, {{/if}} diff --git a/packages/attribute-registry-process/src/services/attributeRegistryService.ts b/packages/attribute-registry-process/src/services/attributeRegistryService.ts index 4c4b9a37f0..dd0ecc681a 100644 --- a/packages/attribute-registry-process/src/services/attributeRegistryService.ts +++ b/packages/attribute-registry-process/src/services/attributeRegistryService.ts @@ -15,6 +15,7 @@ import { AttributeId, AttributeKind, ListResult, + TenantFeatureCertifier, } from "pagopa-interop-models"; import { attributeRegistryApi } from "pagopa-interop-api-clients"; import { toCreateEventAttributeAdded } from "../model/domain/toEvent.js"; @@ -315,7 +316,10 @@ async function getCertifierId( } const certifier = tenant.features - .filter(({ type }) => type === "PersistentCertifier") + .filter( + (feature): feature is TenantFeatureCertifier => + feature.type === "PersistentCertifier" + ) .find(({ certifierId }) => certifierId.trim().length > 0); if (certifier) { diff --git a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts index b33fee3535..9a3e5720e9 100644 --- a/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts +++ b/packages/attribute-registry-process/test/attributeRegistryService.integration.test.ts @@ -6,6 +6,7 @@ import { decodeProtobufPayload, getMockAttribute, getMockAuthData, + getTenantOneCertifierFeature, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -263,7 +264,7 @@ describe("database test", () => { code: "code", kind: attributeKind.certified, creationTime: new Date(writtenPayload.attribute!.creationTime), - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, }; expect(writtenPayload.attribute).toEqual( toAttributeV1(expectedAttribute) @@ -367,7 +368,7 @@ describe("database test", () => { { name: mockAttribute.name, code: "code", - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, description: mockAttribute.description, }, { @@ -397,7 +398,7 @@ describe("database test", () => { code: "code", kind: attributeKind.certified, creationTime: new Date(writtenPayload.attribute!.creationTime), - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, }; expect(writtenPayload.attribute).toEqual( toAttributeV1(expectedAttribute) @@ -405,6 +406,7 @@ describe("database test", () => { expect(writtenPayload.attribute).toEqual(toAttributeV1(attribute)); }); it("should throw attributeDuplicate if an attribute with the same name and code already exists, case insensitive", async () => { + // This test is the same as the previous one, but with a different method const attribute = { ...mockAttribute, name: mockAttribute.name.toUpperCase(), @@ -428,7 +430,7 @@ describe("database test", () => { { name: attribute.name.toLowerCase(), code: attribute.code.toLowerCase(), - origin: tenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(tenant).certifierId, description: attribute.description, }, { diff --git a/packages/backend-for-frontend/.env b/packages/backend-for-frontend/.env index 22387901fa..33fe161b6b 100644 --- a/packages/backend-for-frontend/.env +++ b/packages/backend-for-frontend/.env @@ -12,6 +12,7 @@ CATALOG_PROCESS_URL="http://localhost:3000" ATTRIBUTE_REGISTRY_PROCESS_URL="http://localhost:3200" PURPOSE_PROCESS_URL="http://localhost:3400" AUTHORIZATION_PROCESS_URL="http://localhost:3300" +DELEGATION_PROCESS_URL="http://localhost:3800" TENANT_ALLOWED_ORIGINS="IPA" SAML_AUDIENCE=selfcare.dev.interop.pagopa.it diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts new file mode 100644 index 0000000000..3fa376b4b4 --- /dev/null +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -0,0 +1,108 @@ +import { + bffApi, + catalogApi, + delegationApi, + tenantApi, +} from "pagopa-interop-api-clients"; +import { + DelegationKind, + delegationKind, + delegationState, + DelegationState, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export type DelegationsQueryParams = { + delegatorIds?: string[]; + delegateIds?: string[]; + delegationStates?: delegationApi.DelegationState[]; + kind?: delegationApi.DelegationKind; + eserviceIds?: string[]; +}; + +export function toDelegationState( + state: DelegationState +): bffApi.DelegationState { + return match(state) + .with(delegationState.active, () => bffApi.DelegationState.Values.ACTIVE) + .with( + delegationState.rejected, + () => bffApi.DelegationState.Values.REJECTED + ) + .with(delegationState.revoked, () => bffApi.DelegationState.Values.REVOKED) + .with( + delegationState.waitingForApproval, + () => bffApi.DelegationState.Values.WAITING_FOR_APPROVAL + ) + .exhaustive(); +} + +export function toDelegationKind( + kind: DelegationKind +): delegationApi.DelegationKind { + return match(kind) + .with( + delegationKind.delegatedConsumer, + () => bffApi.DelegationKind.Values.DELEGATED_CONSUMER + ) + .with( + delegationKind.delegatedProducer, + () => bffApi.DelegationKind.Values.DELEGATED_PRODUCER + ) + .exhaustive(); +} + +export function toBffDelegationApiDelegation( + delegation: delegationApi.Delegation, + delegator: tenantApi.Tenant, + delegate: tenantApi.Tenant, + eservice: catalogApi.EService, + producer: tenantApi.Tenant +): bffApi.Delegation { + return { + id: delegation.id, + eservice: { + id: eservice.id, + name: eservice.name, + description: eservice.description, + producerId: eservice.producerId, + producerName: producer.name, + }, + delegate: { + id: delegate.id, + name: delegate.name, + }, + delegator: { + id: delegator.id, + name: delegator.name, + }, + activationContract: delegation.activationContract, + revocationContract: delegation.revocationContract, + submittedAt: delegation.submittedAt, + rejectionReason: delegation.rejectionReason, + state: delegation.state, + kind: delegation.kind, + }; +} + +export function toBffDelegationApiCompactDelegation( + delegation: delegationApi.Delegation, + delegator: tenantApi.Tenant, + delegate: tenantApi.Tenant, + eservice: catalogApi.EService +): bffApi.CompactDelegation { + return { + id: delegation.id, + eserviceName: eservice.name, + delegate: { + name: delegate.name, + id: delegate.id, + }, + delegator: { + name: delegator.name, + id: delegator.id, + }, + state: delegation.state, + kind: delegation.kind, + }; +} diff --git a/packages/backend-for-frontend/src/app.ts b/packages/backend-for-frontend/src/app.ts index c0413917ee..0a5f810292 100644 --- a/packages/backend-for-frontend/src/app.ts +++ b/packages/backend-for-frontend/src/app.ts @@ -29,6 +29,8 @@ import { } from "./utilities/middlewares.js"; import clientRouter from "./routers/clientRouter.js"; import producerKeychainRouter from "./routers/producerKeychainRouter.js"; +import delegationRouter from "./routers/delegationRouter.js"; +import producerDelegationRouter from "./routers/producerDelegationRouter.js"; const serviceName = "backend-for-frontend"; const fileManager = initFileManager(config); @@ -89,7 +91,9 @@ app.use( tenantRouter(zodiosCtx, clients), clientRouter(zodiosCtx, clients), privacyNoticeRouter(zodiosCtx), - producerKeychainRouter(zodiosCtx, clients) + producerKeychainRouter(zodiosCtx, clients), + delegationRouter(zodiosCtx, clients), + producerDelegationRouter(zodiosCtx, clients) ); export default app; diff --git a/packages/backend-for-frontend/src/clients/clientsProvider.ts b/packages/backend-for-frontend/src/clients/clientsProvider.ts index 1c5bd07948..a67bc975e3 100644 --- a/packages/backend-for-frontend/src/clients/clientsProvider.ts +++ b/packages/backend-for-frontend/src/clients/clientsProvider.ts @@ -7,6 +7,7 @@ import { authorizationApi, selfcareV2ClientApi, selfcareV2InstitutionClientBuilder, + delegationApi, } from "pagopa-interop-api-clients"; import { config } from "../config/config.js"; @@ -32,6 +33,11 @@ export type PurposeProcessClient = ReturnType< typeof purposeApi.createPurposeApiClient >; +export type DelegationProcessClient = { + producer: ReturnType; + delegation: ReturnType; +}; + export type AuthorizationProcessClient = { client: ReturnType; producerKeychain: ReturnType< @@ -55,6 +61,7 @@ export type PagoPAInteropBeClients = { purposeProcessClient: PurposeProcessClient; authorizationClient: AuthorizationProcessClient; selfcareV2Client: SelfcareV2Client; + delegationProcessClient: DelegationProcessClient; }; export function getInteropBeClients(): PagoPAInteropBeClients { @@ -89,5 +96,13 @@ export function getInteropBeClients(): PagoPAInteropBeClients { selfcareV2Client: { institution: selfcareV2InstitutionClientBuilder(config), }, + delegationProcessClient: { + producer: delegationApi.createProducerApiClient( + config.delegationProcessUrl + ), + delegation: delegationApi.createDelegationApiClient( + config.delegationProcessUrl + ), + }, }; } diff --git a/packages/backend-for-frontend/src/config/config.ts b/packages/backend-for-frontend/src/config/config.ts index eaa6a49cda..04c1132655 100644 --- a/packages/backend-for-frontend/src/config/config.ts +++ b/packages/backend-for-frontend/src/config/config.ts @@ -99,6 +99,17 @@ export type AuthorizationProcessServerConfig = z.infer< typeof AuthorizationProcessServerConfig >; +export const DelegationProcessServerConfig = z + .object({ + DELEGATION_PROCESS_URL: APIEndpoint, + }) + .transform((c) => ({ + delegationProcessUrl: c.DELEGATION_PROCESS_URL, + })); +export type DelegationProcessServerConfig = z.infer< + typeof DelegationProcessServerConfig +>; + export const S3PrivacyNoticeConfig = z .object({ PRIVACY_NOTICES_CONTAINER: z.string(), @@ -185,6 +196,7 @@ const BffProcessConfig = CommonHTTPServiceConfig.and(TenantProcessServerConfig) .and(PurposeProcessServerConfig) .and(RedisRateLimiterConfig) .and(AuthorizationProcessServerConfig) + .and(DelegationProcessServerConfig) .and(TokenGenerationConfig) .and(SessionTokenGenerationConfig) .and(FileManagerConfig) diff --git a/packages/backend-for-frontend/src/model/errors.ts b/packages/backend-for-frontend/src/model/errors.ts index 2b6970754a..663f561192 100644 --- a/packages/backend-for-frontend/src/model/errors.ts +++ b/packages/backend-for-frontend/src/model/errors.ts @@ -45,9 +45,12 @@ export const errorCodes = { missingActivePurposeVersion: "0036", activeAgreementByEserviceAndConsumerNotFound: "0037", purposeIdNotFoundInClientAssertion: "0038", - clientAssertionPublicKeyNotFound: "0049", + delegationNotFound: "0039", organizationNotAllowed: "0040", cannotGetKeyWithClient: "0041", + clientAssertionPublicKeyNotFound: "0042", + eserviceDelegated: "0043", + delegatedEserviceNotExportable: "0044", }; export type ErrorCodes = keyof typeof errorCodes; @@ -417,3 +420,21 @@ export function cannotGetKeyWithClient( title: "Cannot get key with client", }); } + +export function delegationNotFound(delegationId: string): ApiError { + return new ApiError({ + detail: `Delegation ${delegationId} not found`, + code: "delegationNotFound", + title: "Delegation not found", + }); +} + +export function delegatedEserviceNotExportable( + delegatorId: string +): ApiError { + return new ApiError({ + detail: `Impossibile to export Eservice with a valid delegation for producer ${delegatorId}`, + code: "delegatedEserviceNotExportable", + title: "Delegated Eservice is not exportable", + }); +} diff --git a/packages/backend-for-frontend/src/routers/catalogRouter.ts b/packages/backend-for-frontend/src/routers/catalogRouter.ts index 02f4a12574..9a79390e1b 100644 --- a/packages/backend-for-frontend/src/routers/catalogRouter.ts +++ b/packages/backend-for-frontend/src/routers/catalogRouter.ts @@ -36,6 +36,7 @@ const catalogRouter = ( tenantProcessClient, agreementProcessClient, attributeProcessClient, + delegationProcessClient, }: PagoPAInteropBeClients, fileManager: FileManager ): ZodiosRouter => { @@ -48,6 +49,7 @@ const catalogRouter = ( tenantProcessClient, agreementProcessClient, attributeProcessClient, + delegationProcessClient, fileManager, config ); diff --git a/packages/backend-for-frontend/src/routers/delegationRouter.ts b/packages/backend-for-frontend/src/routers/delegationRouter.ts new file mode 100644 index 0000000000..06d3a10f86 --- /dev/null +++ b/packages/backend-for-frontend/src/routers/delegationRouter.ts @@ -0,0 +1,104 @@ +import { ZodiosRouter } from "@zodios/express"; +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { + ExpressContext, + ZodiosContext, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { bffApi } from "pagopa-interop-api-clients"; +import { unsafeBrandId } from "pagopa-interop-models"; +import { PagoPAInteropBeClients } from "../clients/clientsProvider.js"; +import { fromBffAppContext } from "../utilities/context.js"; +import { delegationServiceBuilder } from "../services/delegationService.js"; +import { makeApiProblem } from "../model/errors.js"; +import { + getDelegationByIdErrorMapper, + getDelegationsErrorMapper, +} from "../utilities/errorMappers.js"; + +const delegationRouter = ( + ctx: ZodiosContext, + { + delegationProcessClient, + tenantProcessClient, + catalogProcessClient, + }: PagoPAInteropBeClients +): ZodiosRouter => { + const delegationRouter = ctx.router(bffApi.delegationsApi.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + + const delegationService = delegationServiceBuilder( + delegationProcessClient, + tenantProcessClient, + catalogProcessClient + ); + + delegationRouter + .get("/delegations", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + const { + limit, + offset, + states, + kind, + delegateIds, + delegatorIds, + eserviceIds, + } = req.query; + + const delegations = await delegationService.getDelegations( + { + limit, + offset, + states, + delegatorIds, + delegateIds, + eserviceIds, + kind, + }, + ctx + ); + + return res + .status(200) + .send(bffApi.CompactDelegations.parse(delegations)); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationsErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving delegations` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .get("/delegations/:delegationId", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + + try { + const delegation = await delegationService.getDelegationById( + unsafeBrandId(req.params.delegationId), + ctx + ); + + return res.status(200).send(bffApi.Delegation.parse(delegation)); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationByIdErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving delegation by id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }); + return delegationRouter; +}; + +export default delegationRouter; diff --git a/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts b/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts new file mode 100644 index 0000000000..97c8a86814 --- /dev/null +++ b/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts @@ -0,0 +1,128 @@ +import { ZodiosRouter } from "@zodios/express"; +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { + ExpressContext, + ZodiosContext, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { bffApi } from "pagopa-interop-api-clients"; +import { unsafeBrandId } from "pagopa-interop-models"; +import { PagoPAInteropBeClients } from "../clients/clientsProvider.js"; +import { fromBffAppContext } from "../utilities/context.js"; +import { emptyErrorMapper, makeApiProblem } from "../model/errors.js"; +import { delegationServiceBuilder } from "../services/delegationService.js"; + +const producerDelegationRouter = ( + ctx: ZodiosContext, + { + delegationProcessClient, + tenantProcessClient, + catalogProcessClient, + }: PagoPAInteropBeClients +): ZodiosRouter => { + const producerDelegationRouter = ctx.router( + bffApi.producerDelegationsApi.api, + { + validationErrorHandler: zodiosValidationErrorToApiProblem, + } + ); + const delegationService = delegationServiceBuilder( + delegationProcessClient, + tenantProcessClient, + catalogProcessClient + ); + + producerDelegationRouter + .post("/producer/delegations", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + const delegationResource = await delegationService.createDelegation( + req.body, + ctx + ); + + return res + .status(200) + .send(bffApi.CreatedResource.parse(delegationResource)); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error creating delegation` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .post("/producer/delegations/:delegationId/approve", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + await delegationService.delegateApproveDelegation( + unsafeBrandId(req.params.delegationId), + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error approving delegation with id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .post("/producer/delegations/:delegationId/reject", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + await delegationService.delegateRejectDelegation( + unsafeBrandId(req.params.delegationId), + req.body, + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error rejecting delegation with id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }) + .delete("/producer/delegations/:delegationId", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + + try { + await delegationService.delegatorRevokeDelegation( + unsafeBrandId(req.params.delegationId), + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error revoke delegation with id ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + }); + + return producerDelegationRouter; +}; + +export default producerDelegationRouter; diff --git a/packages/backend-for-frontend/src/routers/tenantRouter.ts b/packages/backend-for-frontend/src/routers/tenantRouter.ts index a18175cbab..ef647d3b7f 100644 --- a/packages/backend-for-frontend/src/routers/tenantRouter.ts +++ b/packages/backend-for-frontend/src/routers/tenantRouter.ts @@ -405,7 +405,25 @@ const tenantRouter = ( return res.status(errorRes.status).send(errorRes); } } - ); + ) + .post("/tenants/delegatedProducer", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + const tenantId = ctx.authData.organizationId; + + try { + await tenantService.assignTenantDelegatedProducerFeature(tenantId, ctx); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error while assigning delegated producer feature to ${tenantId}` + ); + return res.status(errorRes.status).send(errorRes); + } + }); return tenantRouter; }; diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index 8364212dc9..468a8d66dd 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -32,6 +32,7 @@ import { AgreementProcessClient, AttributeProcessClient, CatalogProcessClient, + DelegationProcessClient, TenantProcessClient, } from "../clients/clientsProvider.js"; import { BffAppContext, Headers } from "../utilities/context.js"; @@ -57,7 +58,10 @@ import { } from "../model/types.js"; import { getAllAgreements, getLatestAgreement } from "./agreementService.js"; import { getAllBulkAttributes } from "./attributeService.js"; -import { assertRequesterIsProducer } from "./validators.js"; +import { + assertNotDelegatedEservice, + assertRequesterIsProducer, +} from "./validators.js"; export type CatalogService = ReturnType; @@ -189,6 +193,7 @@ export function catalogServiceBuilder( tenantProcessClient: TenantProcessClient, agreementProcessClient: AgreementProcessClient, attributeProcessClient: AttributeProcessClient, + delegationProcessClient: DelegationProcessClient, fileManager: FileManager, bffConfig: BffProcessConfig ) { @@ -977,6 +982,12 @@ export function catalogServiceBuilder( }); assertRequesterIsProducer(requesterId, eservice); + await assertNotDelegatedEservice( + delegationProcessClient, + headers, + requesterId, + eserviceId + ); const zipFolderName = `${eservice.id}_${descriptorId}`; const zipFile = await createDescriptorDocumentZipFile( diff --git a/packages/backend-for-frontend/src/services/delegationService.ts b/packages/backend-for-frontend/src/services/delegationService.ts new file mode 100644 index 0000000000..3d127d8873 --- /dev/null +++ b/packages/backend-for-frontend/src/services/delegationService.ts @@ -0,0 +1,287 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import { + bffApi, + catalogApi, + delegationApi, + tenantApi, +} from "pagopa-interop-api-clients"; +import { getAllFromPaginated, WithLogger } from "pagopa-interop-commons"; +import { DelegationId, delegationKind } from "pagopa-interop-models"; +import { + DelegationsQueryParams, + toBffDelegationApiCompactDelegation, + toBffDelegationApiDelegation, + toDelegationKind, +} from "../api/delegationApiConverter.js"; +import { + CatalogProcessClient, + DelegationProcessClient, + TenantProcessClient, +} from "../clients/clientsProvider.js"; +import { delegationNotFound } from "../model/errors.js"; +import { BffAppContext, Headers } from "../utilities/context.js"; + +// eslint-disable-next-line max-params +async function enhanceDelegation< + T extends bffApi.Delegation | bffApi.CompactDelegation +>( + tenantClient: TenantProcessClient, + catalogClient: CatalogProcessClient, + delegation: delegationApi.Delegation, + headers: Headers, + toApiConverter: ( + delegation: delegationApi.Delegation, + delegator: tenantApi.Tenant, + delegate: tenantApi.Tenant, + eservice: catalogApi.EService, + producer: tenantApi.Tenant + ) => T, + cachedTenants: Map = new Map() +): Promise { + const delegator = await getTenantById( + tenantClient, + headers, + delegation.delegatorId, + cachedTenants + ); + + const delegate = await getTenantById( + tenantClient, + headers, + delegation.delegateId, + cachedTenants + ); + + const eservice: catalogApi.EService = await catalogClient.getEServiceById({ + params: { eServiceId: delegation.eserviceId }, + headers, + }); + + // NOTE: If the delegation kind is DELEGATED_PRODUCER, the producer is the same as the delegator tenant. + // In the case of DELEGATED_CONSUMER, the producer can be different. + const producer = + delegation.kind === toDelegationKind(delegationKind.delegatedProducer) + ? await getTenantById( + tenantClient, + headers, + eservice.producerId, + cachedTenants + ) + : delegator; + + return toApiConverter(delegation, delegator, delegate, eservice, producer); +} + +export async function getDelegation( + delegationClient: DelegationProcessClient, + headers: BffAppContext["headers"], + delegationId: DelegationId +): Promise { + const delegation: delegationApi.Delegation = + await delegationClient.delegation.getDelegation({ + params: { delegationId }, + headers, + }); + + if (!delegation) { + throw delegationNotFound(delegationId); + } + return delegation; +} + +export async function getTenantsFromDelegation( + tenantClient: TenantProcessClient, + delegations: delegationApi.Delegation[], + headers: BffAppContext["headers"] +): Promise> { + const tenantIds = delegations.reduce((acc, delegation) => { + acc.add(delegation.delegateId); + acc.add(delegation.delegatorId); + return acc; + }, new Set()); + + const tenants = await Promise.all( + Array.from(tenantIds).map((tenantId) => + tenantClient.tenant.getTenant({ + params: { id: tenantId }, + headers, + }) + ) + ); + + return tenants.reduce((acc, tenant) => { + acc.set(tenant.id, tenant); + return acc; + }, new Map()); +} + +export async function getTenantById( + tenantClient: TenantProcessClient, + headers: BffAppContext["headers"], + tenantId: string, + tenantMap: Map = new Map() +): Promise { + return ( + tenantMap.get(tenantId) ?? + (await tenantClient.tenant.getTenant({ + params: { id: tenantId }, + headers, + })) + ); +} + +export async function getAllDelegations( + delegationProcessClient: DelegationProcessClient, + headers: BffAppContext["headers"], + queryParams: DelegationsQueryParams +): Promise { + return await getAllFromPaginated( + async (offset, limit) => + await delegationProcessClient.delegation.getDelegations({ + headers, + queries: { + ...queryParams, + offset, + limit, + }, + }) + ); +} + +export function delegationServiceBuilder( + delegationClients: DelegationProcessClient, + tenantClient: TenantProcessClient, + catalogClient: CatalogProcessClient +) { + return { + async getDelegationById( + delegationId: DelegationId, + { headers, logger }: WithLogger + ): Promise { + logger.info(`Retrieving delegation with id ${delegationId}`); + + const delegation = await getDelegation( + delegationClients, + headers, + delegationId + ); + + return enhanceDelegation( + tenantClient, + catalogClient, + delegation, + headers, + toBffDelegationApiDelegation + ); + }, + async getDelegations( + { + limit, + offset, + states, + kind, + delegateIds, + delegatorIds, + eserviceIds, + }: { + limit: number; + offset: number; + states?: bffApi.DelegationState[]; + kind?: bffApi.DelegationKind; + delegateIds?: string[]; + delegatorIds?: string[]; + eserviceIds?: string[]; + }, + { headers, logger }: WithLogger + ): Promise { + logger.info("Retrieving all delegations"); + + const delegations = await delegationClients.delegation.getDelegations({ + queries: { + limit, + offset, + delegatorIds, + delegateIds, + delegationStates: states, + kind, + eserviceIds, + }, + headers, + }); + + const involvedTenants = await getTenantsFromDelegation( + tenantClient, + delegations.results, + headers + ); + + const delegationEnanched = await Promise.all( + delegations.results.map((delegation) => + enhanceDelegation( + tenantClient, + catalogClient, + delegation, + headers, + toBffDelegationApiCompactDelegation, + involvedTenants + ) + ) + ); + + return { + results: delegationEnanched, + pagination: { + limit, + offset, + totalCount: delegations.totalCount, + }, + }; + }, + async createDelegation( + createDelegationBody: bffApi.DelegationSeed, + { headers }: WithLogger + ): Promise { + const delegation = + await delegationClients.producer.createProducerDelegation( + createDelegationBody, + { headers } + ); + + return { id: delegation.id }; + }, + async delegatorRevokeDelegation( + delegationId: DelegationId, + { headers }: WithLogger + ): Promise { + return delegationClients.producer.revokeProducerDelegation(undefined, { + params: { + delegationId, + }, + headers, + }); + }, + async delegateRejectDelegation( + delegationId: DelegationId, + rejectBody: bffApi.RejectDelegationPayload, + { headers }: WithLogger + ): Promise { + return delegationClients.producer.rejectProducerDelegation(rejectBody, { + params: { + delegationId, + }, + headers, + }); + }, + async delegateApproveDelegation( + delegationId: DelegationId, + { headers }: WithLogger + ): Promise { + return delegationClients.producer.approveProducerDelegation(undefined, { + params: { + delegationId, + }, + headers, + }); + }, + }; +} diff --git a/packages/backend-for-frontend/src/services/tenantService.ts b/packages/backend-for-frontend/src/services/tenantService.ts index db07d459cd..a545f32ef9 100644 --- a/packages/backend-for-frontend/src/services/tenantService.ts +++ b/packages/backend-for-frontend/src/services/tenantService.ts @@ -418,6 +418,16 @@ export function tenantServiceBuilder( headers, }); }, + async assignTenantDelegatedProducerFeature( + tenantId: TenantId, + { logger, headers }: WithLogger + ): Promise { + logger.info(`Assigning delegated producer feature to tenant ${tenantId}`); + await tenantProcessClient.tenant.assignTenantDelegatedProducerFeature( + undefined, + { headers } + ); + }, }; } diff --git a/packages/backend-for-frontend/src/services/validators.ts b/packages/backend-for-frontend/src/services/validators.ts index ef9e3e8381..1e201fff48 100644 --- a/packages/backend-for-frontend/src/services/validators.ts +++ b/packages/backend-for-frontend/src/services/validators.ts @@ -4,17 +4,30 @@ import { catalogApi, tenantApi, } from "pagopa-interop-api-clients"; -import { TenantId } from "pagopa-interop-models"; +import { + delegationKind, + delegationState, + EServiceId, + TenantId, +} from "pagopa-interop-models"; import { descriptorAttributesFromApi } from "../api/catalogApiConverter.js"; +import { + toDelegationKind, + toDelegationState, +} from "../api/delegationApiConverter.js"; import { tenantAttributesFromApi } from "../api/tenantApiConverter.js"; +import { DelegationProcessClient } from "../clients/clientsProvider.js"; import { + delegatedEserviceNotExportable, invalidEServiceRequester, notValidDescriptor, } from "../model/errors.js"; import { - catalogApiDescriptorState, agreementApiState, + catalogApiDescriptorState, } from "../model/types.js"; +import { BffAppContext } from "../utilities/context.js"; +import { getAllDelegations } from "./delegationService.js"; export function isRequesterEserviceProducer( requesterId: string, @@ -32,6 +45,31 @@ export function assertRequesterIsProducer( } } +export async function assertNotDelegatedEservice( + delegationProcessClient: DelegationProcessClient, + headers: BffAppContext["headers"], + delegatorId: TenantId, + eserviceid: EServiceId +): Promise { + const delegations = await getAllDelegations( + delegationProcessClient, + headers, + { + kind: toDelegationKind(delegationKind.delegatedConsumer), + delegatorIds: [delegatorId], + eserviceIds: [eserviceid], + delegationStates: [ + toDelegationState(delegationState.active), + toDelegationState(delegationState.waitingForApproval), + ], + } + ); + + if (delegations.length > 0) { + throw delegatedEserviceNotExportable(delegatorId); + } +} + export function isAgreementUpgradable( eservice: catalogApi.EService, agreement: agreementApi.Agreement diff --git a/packages/backend-for-frontend/src/utilities/errorMappers.ts b/packages/backend-for-frontend/src/utilities/errorMappers.ts index 55bc8557ef..20174d19c3 100644 --- a/packages/backend-for-frontend/src/utilities/errorMappers.ts +++ b/packages/backend-for-frontend/src/utilities/errorMappers.ts @@ -199,3 +199,11 @@ export const exportEServiceDescriptorErrorMapper = ( .with("notValidDescriptor", () => HTTP_STATUS_BAD_REQUEST) .with("invalidEserviceRequester", () => HTTP_STATUS_FORBIDDEN) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +const delegationNotFoundErrorMapper = (error: ApiError): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getDelegationByIdErrorMapper = delegationNotFoundErrorMapper; +export const getDelegationsErrorMapper = delegationNotFoundErrorMapper; diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index a9909a6a41..1cd5b3c363 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/commons-test/src/eventStoreTestUtils.ts b/packages/commons-test/src/eventStoreTestUtils.ts index b7658f9355..a7910910df 100644 --- a/packages/commons-test/src/eventStoreTestUtils.ts +++ b/packages/commons-test/src/eventStoreTestUtils.ts @@ -2,25 +2,28 @@ import { MessageType } from "@protobuf-ts/runtime"; import { Event } from "pagopa-interop-commons"; import { AgreementEvent, + agreementEventToBinaryData, AgreementId, AttributeEvent, + attributeEventToBinaryData, AttributeId, AuthorizationEvent, + authorizationEventToBinaryData, + catalogEventToBinaryData, ClientId, + DelegationEvent, + delegationEventToBinaryDataV2, + DelegationId, EServiceEvent, EServiceId, ProducerKeychainId, + protobufDecoder, PurposeEvent, + purposeEventToBinaryData, PurposeId, TenantEvent, - TenantId, - agreementEventToBinaryData, - attributeEventToBinaryData, - authorizationEventToBinaryData, - catalogEventToBinaryData, - protobufDecoder, - purposeEventToBinaryData, tenantEventToBinaryData, + TenantId, } from "pagopa-interop-models"; import { IDatabase } from "pg-promise"; import { match } from "ts-pattern"; @@ -31,7 +34,8 @@ type EventStoreSchema = | "catalog" | "tenant" | "purpose" - | '"authorization"'; + | '"authorization"' + | "delegation"; export type StoredEvent = { stream_id: string; @@ -60,6 +64,8 @@ export async function writeInEventstore( ? StoredEvent : T extends '"authorization"' ? StoredEvent + : T extends "delegation" + ? StoredEvent : never, schema: T, postgresDB: IDatabase @@ -90,6 +96,9 @@ export async function writeInEventstore( .with('"authorization"', () => authorizationEventToBinaryData(event.event as AuthorizationEvent) ) + .with("delegation", () => + delegationEventToBinaryDataV2(event.event as DelegationEvent) + ) .exhaustive(), ] ); @@ -108,6 +117,8 @@ export async function readLastEventByStreamId( ? PurposeId : T extends '"authorization"' ? ClientId | ProducerKeychainId + : T extends "delegation" + ? DelegationId : never, schema: T, postgresDB: IDatabase @@ -125,6 +136,8 @@ export async function readLastEventByStreamId( ? PurposeEvent : T extends '"authorization"' ? AuthorizationEvent + : T extends "delegation" + ? DelegationEvent : never > > { @@ -147,6 +160,8 @@ export async function readEventByStreamIdAndVersion( ? PurposeId : T extends '"authorization"' ? ClientId | ProducerKeychainId + : T extends "delegation" + ? DelegationId : never, version: number, schema: T, @@ -165,6 +180,8 @@ export async function readEventByStreamIdAndVersion( ? PurposeEvent : T extends '"authorization"' ? AuthorizationEvent + : T extends "delegation" + ? DelegationEvent : never > > { diff --git a/packages/commons-test/src/setupTestContainersVitest.ts b/packages/commons-test/src/setupTestContainersVitest.ts index b2f6333ced..db9b8990e8 100644 --- a/packages/commons-test/src/setupTestContainersVitest.ts +++ b/packages/commons-test/src/setupTestContainersVitest.ts @@ -168,6 +168,7 @@ export async function setupTestContainersVitest( await readModelRepository?.keys.deleteMany({}); await readModelRepository?.producerKeychains.deleteMany({}); await readModelRepository?.producerKeys.deleteMany({}); + await readModelRepository?.delegations.deleteMany({}); await postgresDB?.none( "TRUNCATE TABLE agreement.events RESTART IDENTITY" @@ -181,6 +182,9 @@ export async function setupTestContainersVitest( await postgresDB?.none( 'TRUNCATE TABLE "authorization".events RESTART IDENTITY' ); + await postgresDB?.none( + "TRUNCATE TABLE delegation.events RESTART IDENTITY" + ); if (s3OriginalBucket && fileManagerConfig && fileManager) { const files = await fileManager.listFiles( diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index ff2f045b04..dbb0143273 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -1,3 +1,4 @@ +import { fail } from "assert"; import { generateMock } from "@anatine/zod-mock"; import { Agreement, @@ -47,6 +48,14 @@ import { AgreementId, PurposeVersionId, ProducerKeychain, + Delegation, + delegationKind, + DelegationId, + DelegationContractDocument, + DelegationContractId, + DelegationState, + TenantFeatureCertifier, + TenantFeature, DescriptorState, GSIPKConsumerIdEServiceId, PlatformStatesAgreementEntry, @@ -61,6 +70,7 @@ import { } from "pagopa-interop-models"; import { AuthData } from "pagopa-interop-commons"; import { z } from "zod"; +import { match } from "ts-pattern"; export function expectPastTimestamp(timestamp: bigint): boolean { return ( @@ -80,6 +90,31 @@ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } +export const getTenantCertifierFeatures = ( + tenant: Tenant +): TenantFeatureCertifier[] => + tenant.features.reduce( + (acc: TenantFeatureCertifier[], feature: TenantFeature) => + match(feature.type) + .with("PersistentCertifier", () => [ + ...acc, + feature as TenantFeatureCertifier, + ]) + .with("DelegatedProducer", () => acc) + .exhaustive(), + [] + ); + +export const getTenantOneCertifierFeature = ( + tenant: Tenant +): TenantFeatureCertifier => { + const certifiedFeatures = getTenantCertifierFeatures(tenant); + if (certifiedFeatures.length === 0) { + fail("Expected certifier feature not found in Tenant"); + } + return certifiedFeatures[0]; +}; + export const getRandomAuthData = ( organizationId: TenantId = generateId() ): AuthData => ({ @@ -331,6 +366,56 @@ export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ selfcareId: generateId(), }); +export const getMockDelegationProducer = ({ + id = generateId(), + delegatorId = generateId(), + delegateId = generateId(), + eserviceId = generateId(), + state = "WaitingForApproval", + activationContract = undefined, + revocationContract = undefined, +}: { + id?: DelegationId; + delegatorId?: TenantId; + delegateId?: TenantId; + eserviceId?: EServiceId; + state?: DelegationState; + activationContract?: DelegationContractDocument; + revocationContract?: DelegationContractDocument; +} = {}): Delegation => { + const creationTime = new Date(); + + return { + id, + delegatorId, + delegateId, + eserviceId, + createdAt: creationTime, + submittedAt: creationTime, + state, + activationContract, + revocationContract, + kind: delegationKind.delegatedProducer, + stamps: { + submission: { + who: delegatorId, + when: creationTime, + }, + }, + }; +}; + +export const getMockDelegationDocument = ( + id?: DelegationContractId +): DelegationContractDocument => ({ + id: id ?? generateId(), + name: "Test document", + prettyName: "Test document", + contentType: "json", + path: "path", + createdAt: new Date(), +}); + export const getMockTokenStatesClientPurposeEntry = ( tokenStateEntryPK?: TokenGenerationStatesClientKidPurposePK ): TokenGenerationStatesClientPurposeEntry => { diff --git a/packages/commons/src/config/kafkaTopicConfig.ts b/packages/commons/src/config/kafkaTopicConfig.ts index 4ada6a4df2..da4918ddb6 100644 --- a/packages/commons/src/config/kafkaTopicConfig.ts +++ b/packages/commons/src/config/kafkaTopicConfig.ts @@ -54,6 +54,15 @@ export const AuthorizationTopicConfig = z })); export type AuthorizationTopicConfig = z.infer; +export const DelegationTopicConfig = z + .object({ + DELEGATION_TOPIC: z.string(), + }) + .transform((c) => ({ + delegationTopic: c.DELEGATION_TOPIC, + })); +export type DelegationTopicConfig = z.infer; + export const KafkaTopicConfig = z.union([ CatalogTopicConfig, AgreementTopicConfig, @@ -61,5 +70,6 @@ export const KafkaTopicConfig = z.union([ AttributeTopicConfig, PurposeTopicConfig, AuthorizationTopicConfig, + DelegationTopicConfig, ]); export type KafkaTopicConfig = z.infer; diff --git a/packages/commons/src/pdf-generator/pdfGenerator.ts b/packages/commons/src/pdf-generator/pdfGenerator.ts index 69e83c958c..e3055740ce 100644 --- a/packages/commons/src/pdf-generator/pdfGenerator.ts +++ b/packages/commons/src/pdf-generator/pdfGenerator.ts @@ -73,7 +73,9 @@ export async function initPDFGenerator(): Promise { ...context, "paged-pdf-polyfill": ``, }); - await page.setContent(htmlCompiled, { waitUntil: "networkidle2" }); + await page.setContent(htmlCompiled, { + waitUntil: "networkidle2", + }); return await page.pdf({ format: "A4", diff --git a/packages/commons/src/repositories/ReadModelRepository.ts b/packages/commons/src/repositories/ReadModelRepository.ts index c1a3ca6333..c19aec0511 100644 --- a/packages/commons/src/repositories/ReadModelRepository.ts +++ b/packages/commons/src/repositories/ReadModelRepository.ts @@ -9,6 +9,7 @@ import { genericInternalError, ProducerKeychainReadModel, ProducerJWKKey, + Delegation, } from "pagopa-interop-models"; import { Collection, @@ -43,6 +44,7 @@ export type ClientKeyCollection = GenericCollection; export type ProducerKeychainCollection = GenericCollection; export type ProducerKeyCollection = GenericCollection; +export type DelegationCollection = GenericCollection; export type Collections = | EServiceCollection @@ -53,7 +55,8 @@ export type Collections = | ClientCollection | ClientKeyCollection | ProducerKeychainCollection - | ProducerKeyCollection; + | ProducerKeyCollection + | DelegationCollection; type BuildQueryKey = `${TPrefix}.${TKey & string}`; @@ -164,6 +167,8 @@ export class ReadModelRepository { public producerKeys: ProducerKeyCollection; + public delegations: DelegationCollection; + private client: MongoClient; private db: Db; @@ -196,6 +201,9 @@ export class ReadModelRepository { this.producerKeys = this.db.collection("producer_keys", { ignoreUndefined: true, }); + this.delegations = this.db.collection("delegations", { + ignoreUndefined: true, + }); } public static init(config: ReadModelDbConfig): ReadModelRepository { diff --git a/packages/compute-agreements-consumer/src/index.ts b/packages/compute-agreements-consumer/src/index.ts index c23c8fb4fc..2fa68f8742 100644 --- a/packages/compute-agreements-consumer/src/index.ts +++ b/packages/compute-agreements-consumer/src/index.ts @@ -94,7 +94,8 @@ async function processMessage({ "MaintenanceTenantDeleted", "TenantMailDeleted", "TenantMailAdded", - "MaintenanceTenantPromotedToCertifier" + "MaintenanceTenantPromotedToCertifier", + "TenantDelegatedProducerFeatureAdded" ), }, () => Promise.resolve() diff --git a/packages/delegation-process/.env b/packages/delegation-process/.env new file mode 100644 index 0000000000..4f8a954421 --- /dev/null +++ b/packages/delegation-process/.env @@ -0,0 +1,30 @@ +HOST=0.0.0.0 +PORT=3800 +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=delegation +EVENTSTORE_DB_USE_SSL=false +AWS_REGION="eu-south-1" + +READMODEL_DB_HOST=localhost +READMODEL_DB_NAME=readmodel +READMODEL_DB_USERNAME=root +READMODEL_DB_PASSWORD=example +READMODEL_DB_PORT=27017 + +WELL_KNOWN_URLS="http://127.0.0.1:4500/jwks.json" +ACCEPTED_AUDIENCES="dev.interop.pagopa.it/ui,refactor.dev.interop.pagopa.it/ui,refactor.dev.interop.pagopa.it/internal,dev.interop.pagopa.it/m2m,refactor.dev.interop.pagopa.it/m2m" + +S3_BUCKET=interop-local-bucket +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +AWS_CONFIG_FILE=aws.config.local + +DELEGATION_DOCUMENT_PATH=delegation diff --git a/packages/delegation-process/Dockerfile b/packages/delegation-process/Dockerfile new file mode 100644 index 0000000000..926e4fab45 --- /dev/null +++ b/packages/delegation-process/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/delegation-process/package.json /app/packages/delegation-process/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/api-clients/package.json /app/packages/api-clients/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/delegation-process /app/packages/delegation-process +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/api-clients /app/packages/api-clients + +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/delegation-process/node_modules \ + package*.json packages/delegation-process/package*.json \ + packages/commons \ + packages/models \ + packages/api-clients \ + packages/delegation-process/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/delegation-process +EXPOSE 3000 + +CMD [ "node", "." ] diff --git a/packages/delegation-process/aws.config.local b/packages/delegation-process/aws.config.local new file mode 100644 index 0000000000..34826a60e2 --- /dev/null +++ b/packages/delegation-process/aws.config.local @@ -0,0 +1,4 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 diff --git a/packages/delegation-process/package.json b/packages/delegation-process/package.json new file mode 100644 index 0000000000..37e1b03a01 --- /dev/null +++ b/packages/delegation-process/package.json @@ -0,0 +1,52 @@ +{ + "name": "pagopa-interop-delegation-process", + "version": "1.0.0", + "description": "PagoPA Interoperability service for delegations management", + "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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", + "@pagopa/eslint-config": "3.0.0", + "@protobuf-ts/runtime": "2.9.4", + "@types/express": "4.17.21", + "@types/node": "20.14.6", + "date-fns": "3.6.0", + "pagopa-interop-commons-test": "workspace:*", + "pg-promise": "11.8.0", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0", + "puppeteer": "22.11.2", + "pdf-lib": "1.17.1" + }, + "dependencies": { + "@zodios/core": "10.9.6", + "@zodios/express": "10.6.1", + "dotenv-flow": "4.1.0", + "express": "4.19.2", + "mongodb": "6.7.0", + "openapi-zod-client": "1.18.1", + "pagopa-interop-api-clients": "workspace:*", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/delegation-process/src/app.ts b/packages/delegation-process/src/app.ts new file mode 100644 index 0000000000..007a7b0706 --- /dev/null +++ b/packages/delegation-process/src/app.ts @@ -0,0 +1,31 @@ +import { + authenticationMiddleware, + contextMiddleware, + loggerMiddleware, + zodiosCtx, + buildJwksClients, +} from "pagopa-interop-commons"; + +import healthRouter from "./routers/HealthRouter.js"; +import delegationProducerRouter from "./routers/DelegationProducerRouter.js"; +import delegationRouter from "./routers/DelegationRouter.js"; +import { config } from "./config/config.js"; + +const serviceName = "delgation-process"; + +const app = zodiosCtx.app(); + +const jwksClients = buildJwksClients(config); + +// 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(healthRouter); +app.use(contextMiddleware(serviceName)); +app.use(authenticationMiddleware(config, jwksClients)); +app.use(loggerMiddleware(serviceName)); +app.use(delegationRouter(zodiosCtx)); +app.use(delegationProducerRouter(zodiosCtx)); + +export default app; diff --git a/packages/delegation-process/src/config/config.ts b/packages/delegation-process/src/config/config.ts new file mode 100644 index 0000000000..1991caba74 --- /dev/null +++ b/packages/delegation-process/src/config/config.ts @@ -0,0 +1,27 @@ +import { + CommonHTTPServiceConfig, + ReadModelDbConfig, + EventStoreConfig, + S3Config, + FileManagerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const DelegationDocumentConfig = z + .object({ + DELEGATION_DOCUMENT_PATH: z.string(), + }) + .transform((c) => ({ + delegationDocumentPath: c.DELEGATION_DOCUMENT_PATH, + })); + +const DelegationProcessConfig = CommonHTTPServiceConfig.and(ReadModelDbConfig) + .and(EventStoreConfig) + .and(S3Config) + .and(FileManagerConfig) + .and(DelegationDocumentConfig); + +export type DelegationProcessConfig = z.infer; +export const config: DelegationProcessConfig = DelegationProcessConfig.parse( + process.env +); diff --git a/packages/delegation-process/src/index.ts b/packages/delegation-process/src/index.ts new file mode 100644 index 0000000000..160d2c4711 --- /dev/null +++ b/packages/delegation-process/src/index.ts @@ -0,0 +1,7 @@ +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "./config/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/delegation-process/src/model/domain/apiConverter.ts b/packages/delegation-process/src/model/domain/apiConverter.ts new file mode 100644 index 0000000000..fd6b0f713a --- /dev/null +++ b/packages/delegation-process/src/model/domain/apiConverter.ts @@ -0,0 +1,145 @@ +import { delegationApi } from "pagopa-interop-api-clients"; +import { + Delegation, + DelegationContractDocument, + DelegationKind, + DelegationStamp, + DelegationStamps, + DelegationState, + delegationKind, + delegationState, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export const delegationToApiDelegation = ( + delegation: Delegation +): delegationApi.Delegation => ({ + id: delegation.id, + delegatorId: delegation.delegatorId, + delegateId: delegation.delegateId, + eserviceId: delegation.eserviceId, + createdAt: delegation.createdAt.toJSON(), + submittedAt: delegation.submittedAt.toJSON(), + approvedAt: delegation.approvedAt + ? delegation.approvedAt.toJSON() + : undefined, + rejectedAt: delegation.rejectedAt + ? delegation.rejectedAt.toJSON() + : undefined, + rejectionReason: delegation.rejectionReason || undefined, + revokedAt: delegation.revokedAt ? delegation.revokedAt.toJSON() : undefined, + state: delegationStateToApiDelegationState(delegation.state), + kind: delegationKindToApiDelegationKind(delegation.kind), + activationContract: delegation.activationContract + ? delegationContractToApiDelegationContract(delegation.activationContract) + : undefined, + revocationContract: delegation.revocationContract + ? delegationContractToApiDelegationContract(delegation.revocationContract) + : undefined, + stamps: delegationStampsToApiDelegationStamps(delegation.stamps), +}); + +export const delegationStateToApiDelegationState = ( + state: DelegationState +): delegationApi.DelegationState => + match(state) + .with( + delegationState.active, + () => delegationApi.DelegationState.Values.ACTIVE + ) + .with( + delegationState.rejected, + () => delegationApi.DelegationState.Values.REJECTED + ) + .with( + delegationState.revoked, + () => delegationApi.DelegationState.Values.REVOKED + ) + .with( + delegationState.waitingForApproval, + () => delegationApi.DelegationState.Values.WAITING_FOR_APPROVAL + ) + .exhaustive(); + +export const delegationKindToApiDelegationKind = ( + kind: DelegationKind +): delegationApi.DelegationKind => + match(kind) + .with( + delegationKind.delegatedConsumer, + () => delegationApi.DelegationKind.Values.DELEGATED_CONSUMER + ) + .with( + delegationKind.delegatedProducer, + () => delegationApi.DelegationKind.Values.DELEGATED_PRODUCER + ) + .exhaustive(); + +export const apiDelegationKindToDelegationKind = ( + kind: delegationApi.DelegationKind +): DelegationKind => + match(kind) + .with( + delegationApi.DelegationKind.Values.DELEGATED_CONSUMER, + () => delegationKind.delegatedConsumer + ) + .with( + delegationApi.DelegationKind.Values.DELEGATED_PRODUCER, + () => delegationKind.delegatedProducer + ) + .exhaustive(); + +export const delegationContractToApiDelegationContract = ( + contract: DelegationContractDocument +): delegationApi.DelegationContractDocument => ({ + id: contract.id, + name: contract.name, + prettyName: contract.prettyName, + contentType: contract.contentType, + path: contract.path, + createdAt: contract.createdAt.toJSON(), +}); + +export const delegationStampsToApiDelegationStamps = ( + stamps: DelegationStamps +): delegationApi.DelegationStamps => ({ + submission: delegationStampToApiDelegationStamp(stamps.submission), + activation: stamps.activation + ? delegationStampToApiDelegationStamp(stamps.activation) + : undefined, + rejection: stamps.rejection + ? delegationStampToApiDelegationStamp(stamps.rejection) + : undefined, + revocation: stamps.revocation + ? delegationStampToApiDelegationStamp(stamps.revocation) + : undefined, +}); + +export const delegationStampToApiDelegationStamp = ( + stamp: DelegationStamp +): delegationApi.DelegationStamp => ({ + who: stamp.who, + when: stamp.when.toJSON(), +}); + +export const apiDelegationStateToDelegationState = ( + state: delegationApi.DelegationState +): DelegationState => + match(state) + .with( + delegationApi.DelegationState.Values.ACTIVE, + () => delegationState.active + ) + .with( + delegationApi.DelegationState.Values.REJECTED, + () => delegationState.rejected + ) + .with( + delegationApi.DelegationState.Values.REVOKED, + () => delegationState.revoked + ) + .with( + delegationApi.DelegationState.Values.WAITING_FOR_APPROVAL, + () => delegationState.waitingForApproval + ) + .exhaustive(); diff --git a/packages/delegation-process/src/model/domain/errors.ts b/packages/delegation-process/src/model/domain/errors.ts new file mode 100644 index 0000000000..b5b0064bb6 --- /dev/null +++ b/packages/delegation-process/src/model/domain/errors.ts @@ -0,0 +1,144 @@ +import { + ApiError, + Delegation, + EServiceId, + makeApiProblemBuilder, + TenantId, + DelegationState, +} from "pagopa-interop-models"; + +export const errorCodes = { + delegationNotFound: "0001", + eserviceNotFound: "0002", + delegationAlreadyExists: "0003", + tenantNotFound: "0004", + invalidDelegatorAndDelegateIds: "0005", + invalidExternalOriginId: "0006", + tenantNotAllowedToDelegation: "0007", + delegationNotRevokable: "0008", + operationNotAllowOnDelegation: "0009", + operationRestrictedToDelegate: "0010", + incorrectState: "0011", + differentEserviceProducer: "0012", +}; + +export type ErrorCodes = keyof typeof errorCodes; + +export const makeApiProblem = makeApiProblemBuilder(errorCodes); + +export function delegationNotFound(delegationId: string): ApiError { + return new ApiError({ + detail: `Delegation ${delegationId} not found`, + code: "delegationNotFound", + title: "Delegation not found", + }); +} + +export function delegationAlreadyExists( + delegatorId: string, + eserviceId: string, + delegationKind: string +): ApiError { + return new ApiError({ + detail: `Delegation type ${delegationKind} already exists for EService ${eserviceId} by delegator ${delegatorId}`, + code: "delegationAlreadyExists", + title: "Delegation already exists", + }); +} + +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", + }); +} + +export function delegatorAndDelegateSameIdError(): ApiError { + return new ApiError({ + detail: `Error occurs because Delegator and Delegate have the same Id`, + code: "invalidDelegatorAndDelegateIds", + title: "Invalid Delegator and Delegate", + }); +} + +export function invalidExternalOriginError( + externalOrigin?: string +): ApiError { + return new ApiError({ + detail: `Delegator is not an IPA`, + code: "invalidExternalOriginId", + title: `Invalid External origin ${externalOrigin}`, + }); +} + +export function tenantNotAllowedToDelegation( + tenantId: string +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} not allowed to delegation`, + code: "tenantNotAllowedToDelegation", + title: "Tenant not allowed to delegation", + }); +} + +export function delegationNotRevokable( + delegation: Delegation +): ApiError { + return new ApiError({ + detail: `Delegation ${delegation.id} is not revokable. State: ${delegation.state}`, + code: "delegationNotRevokable", + title: "Delegation not revokable", + }); +} + +export function delegatorNotAllowToRevoke( + delegation: Delegation +): ApiError { + return new ApiError({ + detail: `Requester ${delegation.id} is not delegator for the current delegation with id ${delegation.id}`, + code: "operationNotAllowOnDelegation", + title: "Requester and delegator are differents", + }); +} + +export function operationRestrictedToDelegate( + tenantId: string, + delegationId: string +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} is not a delegate for delegation ${delegationId}`, + code: "operationRestrictedToDelegate", + title: "Operation restricted to delegate", + }); +} + +export function incorrectState( + delegationId: string, + actualState: DelegationState, + expectedState: DelegationState +): ApiError { + return new ApiError({ + detail: `Delegation ${delegationId} is in state ${actualState} but expected ${expectedState}`, + code: "incorrectState", + title: "Incorrect state", + }); +} + +export function differentEServiceProducer( + requesterId: string +): ApiError { + return new ApiError({ + detail: `Eservice producer if different from requester with id ${requesterId}`, + code: "differentEserviceProducer", + title: "Operation not allowed", + }); +} diff --git a/packages/delegation-process/src/model/domain/models.ts b/packages/delegation-process/src/model/domain/models.ts new file mode 100644 index 0000000000..216dba9c8f --- /dev/null +++ b/packages/delegation-process/src/model/domain/models.ts @@ -0,0 +1,14 @@ +import { + DelegationKind, + DelegationState, + EServiceId, + TenantId, +} from "pagopa-interop-models"; + +export type GetDelegationsFilters = { + eserviceId?: EServiceId; + delegatorId?: TenantId; + delegateId?: TenantId; + delegationKind?: DelegationKind; + states?: DelegationState[]; +}; diff --git a/packages/delegation-process/src/model/domain/toEvent.ts b/packages/delegation-process/src/model/domain/toEvent.ts new file mode 100644 index 0000000000..8241c4daf9 --- /dev/null +++ b/packages/delegation-process/src/model/domain/toEvent.ts @@ -0,0 +1,81 @@ +import { CreateEvent } from "pagopa-interop-commons"; +import { + CorrelationId, + Delegation, + DelegationEventV2, + WithMetadata, + toDelegationV2, +} from "pagopa-interop-models"; + +export function toCreateEventProducerDelegationSubmitted( + delegation: Delegation, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.id, + version: 0, + event: { + type: "ProducerDelegationSubmitted", + event_version: 2, + data: { + delegation: toDelegationV2(delegation), + }, + }, + correlationId, + }; +} + +export function toCreateEventProducerDelegationRevoked( + delegation: Delegation, + version: number, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.id, + version, + event: { + type: "ProducerDelegationRevoked", + event_version: 2, + data: { + delegation: toDelegationV2(delegation), + }, + }, + correlationId, + }; +} + +export function toCreateEventProducerDelegationApproved( + delegation: WithMetadata, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.data.id, + version: delegation.metadata.version, + event: { + type: "ProducerDelegationApproved", + event_version: 2, + data: { + delegation: toDelegationV2(delegation.data), + }, + }, + correlationId, + }; +} + +export function toCreateEventProducerDelegationRejected( + delegation: WithMetadata, + correlationId: CorrelationId +): CreateEvent { + return { + streamId: delegation.data.id, + version: delegation.metadata.version, + event: { + type: "ProducerDelegationRejected", + event_version: 2, + data: { + delegation: toDelegationV2(delegation.data), + }, + }, + correlationId, + }; +} diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Bold.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..55e0b1a5533e4d1474be0ed37b5c7935dafef6c0 GIT binary patch literal 198612 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%0A#iYtkm8@D`eW89r_55?_^ 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 < zS?zq}eC+(qS>ycOS?m16`Na9Bv(EVxe?HOQ-<)k!z?k8W{!r$Xu?dcrdOz)=?I#=i6A94k{5dV-X)+M@B zCv|h(LbudqTK*;127fPUC;ySd2=@TpL3h-hbZ32_?xMTugD~dBzfSOHx$gK5*P-|d zS0%o|RfR8b)#zUM_EsIfymdIfyLANqTyhltW_&chwRMc{kAIg8#P_xa>B0IqeLVhN zG8F$cK2e{fPu9cmPrBiH1irxa1AUr4U5~_9xJKa{TxaSZ>M{B({L^Hu{)heqe=}Q$ zKbw4tKbd_Ye=w83(&FzX_#Z9)Zt@Mj&-D@hP=>$I;@@OD^mlrv{Fe-WpT$oIT^s); z)A)zfFY*7XJMayxyWM;6->Pfe-?%s6KUF`)AEofm5%*^Ni{uvfR{VJce?P?^Qy;}& zs&2#IsqnQd`3EZgfcg~vBK3^>toxk%d-r+#OKK7R`m`8-dwL6hd0K+MJ1xauoi@8) zxm(gPmV}_ztPV1aD|F69>53{PeviRBi)IImT5*dq0 z5sMiC2~ET%ibI1M!H9^63dp45gfpU1g9Soq3^6tmR0Kp+K%|u@0xBSrG9xIC5NC)e zA_4*eLjU%uq9}=UVm`mG`;XSIR=s=Qy|?ar_w0T4S?h3CT7&w(Ce?qw)&6Onv~F6D z`oBTikovz-+Bj{(2%u^Di?mt#Z)x+iU3y&FExkDHp7uz4roGbMc>JjALH$*aJYx<& zS!XVmWnnKmkzFsx=cfwpJ~xs72DvAx&L+62j3TGuQP~MU%C)Xb^rP&GBjgmVL)n%$V7Rw9fRVUMM)Tu8sSOKny%ruWVdBuF?WR9)PUe}5u*}EJ zt*0z7?>1Rter>WWxh}a*UN^^IS(&U&*2$`5SMah1*RX1`4yUkuSs!YL+VX{YfytMw zJ=k0};^*}%*&0p^tz;V>UTtN&d2-2~Fg}czysFbPU*a+s`w3)MW0eyAEp zqnh9hwOK2Kt>JsEoMzIj<};@3s{(Ufum(V)(|YnDNMVtQs6Mw1{}j$fRk`lnvpXbPwpi-D{<6b z8y%E3kyUn)Jpxa;A|6RK@fd8x>V3!K{MQ;czxFuvbxC^Sve!4c5vRNXWRiQy7bD0m z>7#YU${3eqVW`x;cL0Ezd*xO-k_#}KDHid0r7c50c%cK>_ z6*b8VjmQDVr>)Y~>6vMJo?aI+K%ca4dSiNPIv_1fho-~Qhte_W6X~RMN;)l_na)cW zr7P0a>HFzN>H2hIx+UG2?#<+6ax>*KRWda)wKEMfO*6-3T4h>i&djvWbjozg^v+z7 z>7N;#8I~EFc_uS6vn2CwW_@N`W>?l_L$*w|VzyegX0~p&(U0xRHp{lmo}4``+b-K7 z+c}H7z1ylE5Tm1;!&KkPvcIYSIPW=@8PFY*i?b3DNZg^gGdszEc-S_%A zxK}rT`}7OEX`Oxv3ZrV$k$wew6x11k0WQDA*v z6KnuCpwiIS0gv%@!A8Cw*x1(xoA?IcvA!YL)E@)>!Z!k&c^r9JjlMb9+@A_ke}OIh z@gQ{=NG%3ZkAc)=Aaxl?Z3a@Gfz)UqbsBhz{}1p~-x_3fP_Q+t1uJ>`bnrCa0X*HG z2cF?Ofxq_WgJ=3~U|Uv0S83-j0MBBzV3ptai@>uzs+P3(UBPqWksV+}RL(`gQR#@= zuDa>|0bZZy^TAI3Veotp!zP{mBebl!a)BSk)`fmFc#(e;?Bd6QUHxNVH$M)%*gp<- z_fLR5SQ}lXrymdY@)N+`ej<2@e+s%C9j3b$~^n-f0o21r~ui&?1m5|0#HvMIfuft^-fI!a+yo*}hgt;cA1wmuDTqM*lSQE3ZxN{fX%VQyECTfbi$MAkB2fQq5y*P9 zJHQ7m0`(z_Kz-O^Pe)qp=_3|hl9)M8J^TI}g#7JE9*Vox8p*wZI0 z_Vh`MJsoecrxPspbfU$cK4r0|lPvad3_JpyY_Z4sw>7|LEcWzSi#?rUv8R8rmZ#6f z2>jF{N}u=bz^VQ$@C6t-)oK20aJp{~&hY1eFIqh4Oa5H&WjH$ZJ#sGL^-R{~Rz1bq zw9c~Dth22(>nqlpwXM#v)~s`_HS0V-2ApqgS{GQG)>o|!;r2)y(uLNBbdj|oU2JVg zmslIprPhXYnYAHZZf!_cSR2yUtqti)YeTxq+K|3sZAjm=Hl(Yq4dIz9&WMY@oQZ~-?^TedRNF{YjN^s@;B=1-2qmS%6oP4IX&v005eY&{#LSq zy8C+w{6L@kcJc)^_}-9VYkBfc@+I~7zK~^W1-qhr+C!#-i3#ACAS;L<|EzS=+(TBwU3WMKAD}Y5yic>mgJjn zt<=If_H3!0@}JaCbJJXD5OtLt#v%FcT31k9W!C5OqiRxdQZUB4lQ@mvgzd}W9p!Uu zWS_0G%{~+xR@-KOIdqfH!g?yBFR5fVh0Wm`YS|rOci0oY4}0wx854i3nk~A%pxPAA z-icR7nPIz-KXhr5BZW8yzo-pttz@-KHns*t{SV93wLvgeY0G#h>hjNh22h#g6Ds|JwNUP@n&RQoq@i ziT7MS-t&?1o@>N=uF3N$9*amLR}OyfWIk_4R#U$g1$UU6>|S##QCc>l7nkRWXJ}n{ zf!L%~dYKZ zu@Y&+QrGAfkGMD<(LEkPw&VzVR>T3Ln+6s zTlVIViRX|_nWI^9jpDP&y!-bl%yL!w%=dHFz)Rgh|7e_40X^fOVqU6{K5;)QHIyFl zKF+O5HtwHOz>4!3GHZn|L@(&ZVbAFG<&He}HN9OGVjk2)K=KHfh`I3O6?owTE!=5&)GPLn5$ju_!&%(wByT` z>^HvO?yn9{S*RSoE8Z@;ldz8%Wj{O$`vX6A)w=O)i^3x1z`>AN+Y!0D#7?_|9Rm1pdG+8Tw*`$wO(qV+|3aO+N-rx`%ZB1rjpBsYP=SFwD(^_-2Qu9 zl&m`F-mSM2Dx!5i+)wK-x~GGV=aZ4yi|&=P*08<*uc+oR=KVlF)Q|LIxS3D&GhL_a z^>45=8}tkPQa9>X@HJoSX69jR(XB8y-|BYVp*xuu^Y_X`rj$03!CyPq9|p&BxG(F= z;S5m$wx^QM^Ob!SUlrb`I$qmH`WpTy7@(uQX&H53f)1?;Fg87eE^x3a&<>{XU=5^5 zC+PMgNjy9Sm;FpX3s>@2V8iD6 zd49fM;9rFoTj&@0#eRuj3PZNsukbBOanGeW<{qJE=oNZLu6Z@yB<*6&Y5n>Z!UBHL zZ83h)ZG}NR$L$F7!aVWx!WqelHJjA~Me+P|x1<9a*H3Ln`TtZ)`Y8(2-|K0I(wzQx zTGD}bH0J+UOUh-`v5^L{2`!)x5@V-^q0u39lMalcIx>1H z(Mx{jTC@Tqm)b-`y+GY%{5p9XejOui_;sp8#xm69Fzc?OG3&09G3!*CFzc>zm>cH0 zDzSpE8Y}QRv6^iVtJ&tUn*C+0W?Pi%9~g%e^$&OK!UtmMjJ zQbrD!vT@dH1*s^NBu^?!6{(8DP<1&%j%4QLQBqTmmVButwWW^Km3mTN8c0KYh#E;_ z=4Bo$O>rM;hAUBXJc(M!@$&D|5+|Y)Sh`CO=_$RK`+136DwjzgxtxFH zaHU)&S4&^HMy{3X1>bq&}jf zbhM7qM|G?|rsMQ+eL|l^0hyo^nSVG*pGFJ$FMURz)hYNXJ%^vtRDD6G>2&mw7xg85 zS!e1jl#*9;j?UG2Iv>sCReent>LOi?da_iP>2h76ucM=^(l_)?U9E4SsJw$O)BkJ8 z|Gb9$@j)u`h7uil(?Lq|KT?q&E>@67Cu5SaXvmKx3JDzlC%{aVC?6x9K zt)Bcx+)hC;x6?hcIz1w*b7y3A21izBcw}{^L{?{UmEQ z%~56D5>?i+)Lf0x4NPTi7FE_?MU}NpR9R1pDr<+RvYs1N)*GlaR!IM-vJPPka*Y&5 zmGxfZh2*}dwf+gMwE&gXwASHKYkfFsts|qp`bgAQM@4;gT+~;ei2CY;sIN|p`s!2Y ztBFiv1X31#wL+37&qg)%dB!#QD60+8QKv@*b!OB{XGOhqe$-1}je6;#sFyAdtHLT- zQj!m;s;O*9dtpGyKCAo{MXVotG!z8b9y=mtR~Kt)$H+i$x%`TLmR7Iq*Q>An+X8Yi VGt1clU7|DjWg`+aAKPR5{~Ih^NSy!x literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Italic.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..be99e1c22cf75a0ca043d1091295795ba67a989b GIT binary patch literal 202912 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 &CB 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

l8tD<~9T^-M9vKxG6WJ-U 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 literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Medium.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0f0fd1d55b3e9e32ac0f1b1ecdbc59612ca88bf5 GIT binary patch literal 198616 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 literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-Regular.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1cd025961bc821fcbc63005dca001a4a963afb56 GIT binary patch literal 198552 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%0D)?#moV{!aX28%GAYPDSuRezXi~ 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 V=o#4tPSErC$9f&`I=02O|0lUgEYko0 literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/media/logo_pagopa.png b/packages/delegation-process/src/resources/assets/media/logo_pagopa.png new file mode 100644 index 0000000000000000000000000000000000000000..76b9f55b26bd82fa771e2387d21b2ab1d15a546e GIT binary patch literal 4514 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 A`v3p{ literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg b/packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg new file mode 100644 index 0000000000..52e708741e --- /dev/null +++ b/packages/delegation-process/src/resources/assets/media/pictogram_pagopa.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html new file mode 100644 index 0000000000..603eb98290 --- /dev/null +++ b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html @@ -0,0 +1,87 @@ + + + + + + + Deroga all'erogazione + + {{{paged-pdf-polyfill}}} + + + + + + + +
+ +
+ + +

Deroga all'erogazione

+ + + +
+

Registrazione

+ +
+ In data {{submissionDate}} alle ore + {{submissionTime}}, l’Infrastruttura ha trasmesso al + delegato la richiesta di delega del delegante. +
+ +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’Infrastruttura ha ricevuto la + dichiarazione di accettazione della delega (di seguito “dischiarazione + di accettazione”) inviata dal delegato all'erogazione, tramite + l'operatore amministrativo con identificativo Area Riservata + {{activatorId}}. +
+ +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’Infrastruttura ha comunicato al + delegato all'erogazione la dichiarazione di accettazione. +
+ +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’Infrastruttura ha registrato il + delegato all'erogazione quale soggetto titolato ad erogare l'e-service + per conto del delegante. +
+
+ + diff --git a/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html new file mode 100644 index 0000000000..e36b2480cc --- /dev/null +++ b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html @@ -0,0 +1,80 @@ + + + + + + + Revoca della delega all’erogazione + + {{{paged-pdf-polyfill}}} + + + + + + + +
+ +
+ + +

Revoca della delega all’erogazione

+ + + +
+

Registrazione

+ +
+ In data {{revocationDate}} alle ore + {{revocationTime}}, l’infrastruttura ha ricevuto la + dichiarazione di revoca della delega (di seguito “dichiarazione di + revoca”) inviata dal delegante, tramite l’operatore amministrativo con + identificativo Area Riservata {{submitterId}}. +
+ +
+ In data {{revocationDate}} alle ore + {{revocationTime}}, l’Infrastruttura ha comunicato al + delegante e al delegato all’erogazione la dichiarazione di revoca. +
+ +
+ In data {{revocationDate}} alle ore + {{revocationTime}}, l’infrastruttura ha registrato la + revoca al delegato all’erogazione quale soggetto titolato ad erogare + l’e-service per conto del delegante. +
+
+ + diff --git a/packages/delegation-process/src/resources/templates/delegationTemplate.css b/packages/delegation-process/src/resources/templates/delegationTemplate.css new file mode 100644 index 0000000000..4e09620a97 --- /dev/null +++ b/packages/delegation-process/src/resources/templates/delegationTemplate.css @@ -0,0 +1,130 @@ +/* Load fonts */ +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Regular.ttf") format("truetype"); + font-weight: 400; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Italic.ttf") format("truetype"); + font-weight: 400; + font-style: italic; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Medium.ttf") format("truetype"); + font-weight: 500; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-SemiBold.ttf") format("truetype"); + font-weight: 600; +} + +@font-face { + font-family: "Montserrat"; + src: url("../assets/font/Montserrat-Bold.ttf") format("truetype"); + font-weight: 700; +} + +@page { + margin: 1.2cm 2cm 1.4cm 2cm; + size: a4; + + @top-center { + content: element(header); + vertical-align: top; + } + + @bottom-center { + content: element(footer); + vertical-align: bottom; + } +} + +.header { + position: running(header); +} + +.footer { + position: running(footer); +} + +* { + box-sizing: border-box; +} + +body { + font-family: "Montserrat", arial, sans-serif; + font-size: 12px; + line-height: 18px; + margin: 0; +} + +strong { + font-weight: 600; +} + +.header img { + width: 85px; +} + +.header { + text-align: left; +} + +h1 { + font-size: 14px; + line-height: 16px; + margin: 0 0 16px; + font-weight: 700; +} + +h2 { + font-size: 12px; + line-height: 15px; + margin: 16px 0 8px; + font-weight: 700; +} + +.footer hr { + border: 0; + border-top: 1px solid #e3e7eb; + margin: 8px 0; + padding: 0; + height: 1px; +} + +.footer .page-number:after { + content: counter(page) " di " counter(pages); + color: #898989; +} + +.footer .pagopa { + content: ""; + display: block; + clear: both; +} + +.footer .pagopa-data { + float: left; + text-align: left; + color: #475a6d; + display: inline-block; +} + +.footer .pictogram { + float: right; +} + +.footer { + font-size: 6px; + line-height: 7.5px; +} + +.content > div { + margin-bottom: 8px; +} diff --git a/packages/delegation-process/src/routers/DelegationProducerRouter.ts b/packages/delegation-process/src/routers/DelegationProducerRouter.ts new file mode 100644 index 0000000000..8e1dd50659 --- /dev/null +++ b/packages/delegation-process/src/routers/DelegationProducerRouter.ts @@ -0,0 +1,179 @@ +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { delegationApi } from "pagopa-interop-api-clients"; +import { + ExpressContext, + fromAppContext, + ReadModelRepository, + userRoles, + ZodiosContext, + zodiosValidationErrorToApiProblem, + authorizationMiddleware, + initDB, + initPDFGenerator, + initFileManager, +} from "pagopa-interop-commons"; +import { unsafeBrandId } from "pagopa-interop-models"; +import { readModelServiceBuilder } from "../services/readModelService.js"; +import { config } from "../config/config.js"; +import { delegationToApiDelegation } from "../model/domain/apiConverter.js"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { delegationProducerServiceBuilder } from "../services/delegationProducerService.js"; +import { + createProducerDelegationErrorMapper, + revokeDelegationErrorMapper, + approveDelegationErrorMapper, + rejectDelegationErrorMapper, +} from "../utilites/errorMappers.js"; + +const readModelService = readModelServiceBuilder( + ReadModelRepository.init(config) +); + +const pdfGenerator = await initPDFGenerator(); +const fileManager = initFileManager(config); + +const delegationProducerService = delegationProducerServiceBuilder( + initDB({ + username: config.eventStoreDbUsername, + password: config.eventStoreDbPassword, + host: config.eventStoreDbHost, + port: config.eventStoreDbPort, + database: config.eventStoreDbName, + schema: config.eventStoreDbSchema, + useSSL: config.eventStoreDbUseSSL, + }), + readModelService, + pdfGenerator, + fileManager +); + +const { ADMIN_ROLE } = userRoles; + +const delegationProducerRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const delegationProducerRouter = ctx.router(delegationApi.producerApi.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + + delegationProducerRouter + .post( + "/producer/delegations", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + const delegation = + await delegationProducerService.createProducerDelegation( + req.body, + ctx + ); + return res + .status(200) + .json( + delegationApi.Delegation.parse( + delegationToApiDelegation(delegation) + ) + ); + } catch (error) { + const errorRes = makeApiProblem( + error, + createProducerDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .delete( + "/producer/delegations/:delegationId", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + const { delegationId } = req.params; + await delegationProducerService.revokeProducerDelegation( + unsafeBrandId(delegationId), + ctx + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + revokeDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .post( + "/producer/delegations/:delegationId/approve", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId } = req.params; + + try { + await delegationProducerService.approveProducerDelegation( + ctx.authData.organizationId, + unsafeBrandId(delegationId), + ctx.correlationId, + ctx.logger + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + approveDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .post( + "/producer/delegations/:delegationId/reject", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId } = req.params; + const { rejectionReason } = req.body; + + try { + await delegationProducerService.rejectProducerDelegation( + ctx.authData.organizationId, + unsafeBrandId(delegationId), + ctx.correlationId, + rejectionReason + ); + + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + rejectDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ); + + return delegationProducerRouter; +}; + +export default delegationProducerRouter; diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts new file mode 100644 index 0000000000..99f603305a --- /dev/null +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -0,0 +1,137 @@ +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { delegationApi } from "pagopa-interop-api-clients"; +import { + ExpressContext, + ReadModelRepository, + ZodiosContext, + authorizationMiddleware, + fromAppContext, + userRoles, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { EServiceId, TenantId, unsafeBrandId } from "pagopa-interop-models"; +import { readModelServiceBuilder } from "../services/readModelService.js"; +import { config } from "../config/config.js"; +import { + apiDelegationKindToDelegationKind, + apiDelegationStateToDelegationState, + delegationToApiDelegation, +} from "../model/domain/apiConverter.js"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { getDelegationErrorMapper } from "../utilites/errorMappers.js"; +import { delegationServiceBuilder } from "../services/delegationService.js"; + +const readModelService = readModelServiceBuilder( + ReadModelRepository.init(config) +); + +const delegationService = delegationServiceBuilder(readModelService); + +const { ADMIN_ROLE, API_ROLE, SECURITY_ROLE, M2M_ROLE, SUPPORT_ROLE } = + userRoles; + +const delegationRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const delegationRouter = ctx.router(delegationApi.delegationApi.api, { + validationErrorHandler: zodiosValidationErrorToApiProblem, + }); + + delegationRouter + .get( + "/delegations", + authorizationMiddleware([ + ADMIN_ROLE, + API_ROLE, + SECURITY_ROLE, + M2M_ROLE, + SUPPORT_ROLE, + ]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { + offset, + limit, + delegateIds, + delegatorIds, + eserviceIds, + delegationStates, + kind, + } = req.query; + + try { + const delegations = await delegationService.getDelegations({ + delegateIds: delegateIds.map(unsafeBrandId), + delegatorIds: delegatorIds.map(unsafeBrandId), + delegationStates: delegationStates.map( + apiDelegationStateToDelegationState + ), + eserviceIds: eserviceIds.map(unsafeBrandId), + kind: kind && apiDelegationKindToDelegationKind(kind), + offset, + limit, + }); + + return res.status(200).send( + delegationApi.Delegations.parse({ + results: delegations.map((delegation) => + delegationToApiDelegation(delegation) + ), + totalCount: delegations.length, + }) + ); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ) + .get( + "/delegations/:delegationId", + authorizationMiddleware([ + ADMIN_ROLE, + API_ROLE, + SECURITY_ROLE, + M2M_ROLE, + SUPPORT_ROLE, + ]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId } = req.params; + + try { + const delegation = await delegationService.getDelegationById( + unsafeBrandId(delegationId) + ); + + return res + .status(200) + .send( + delegationApi.Delegation.parse( + delegationToApiDelegation(delegation) + ) + ); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ); + + return delegationRouter; +}; + +export default delegationRouter; diff --git a/packages/delegation-process/src/routers/HealthRouter.ts b/packages/delegation-process/src/routers/HealthRouter.ts new file mode 100644 index 0000000000..6e305f570f --- /dev/null +++ b/packages/delegation-process/src/routers/HealthRouter.ts @@ -0,0 +1,8 @@ +import { zodiosRouter } from "@zodios/express"; +import { delegationApi } from "pagopa-interop-api-clients"; + +const healthRouter = zodiosRouter(delegationApi.healthApi.api); + +healthRouter.get("/status", async (_, res) => res.status(200).send()); + +export default healthRouter; diff --git a/packages/delegation-process/src/services/delegationContractBuilder.ts b/packages/delegation-process/src/services/delegationContractBuilder.ts new file mode 100644 index 0000000000..1e20e21f4e --- /dev/null +++ b/packages/delegation-process/src/services/delegationContractBuilder.ts @@ -0,0 +1,185 @@ +import { fileURLToPath } from "url"; +import path from "path"; +import { + dateAtRomeZone, + FileManager, + formatDateyyyyMMddHHmmss, + Logger, + PDFGenerator, + timeAtRomeZone, +} from "pagopa-interop-commons"; +import { + Delegation, + DelegationContractDocument, + DelegationContractId, + EService, + generateId, + Tenant, +} from "pagopa-interop-models"; +import { DelegationProcessConfig } from "../config/config.js"; + +const CONTENT_TYPE_PDF = "application/pdf"; +const DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME = "Delega"; +const DELEGATION_REVOCATION_CONTRACT_PRETTY_NAME = "Revoca della delega"; + +const createDelegationDocumentName = ( + documentCreatedAt: Date, + documentType: "activation" | "revocation" +): string => + `${formatDateyyyyMMddHHmmss( + documentCreatedAt + )}_delegation_${documentType}_contract.pdf`; + +const filename = fileURLToPath(import.meta.url); +const dirname = path.dirname(filename); + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const contractBuilder = { + createActivationContract: async ({ + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + }: { + delegation: Delegation; + delegator: Tenant; + delegate: Tenant; + eservice: EService; + pdfGenerator: PDFGenerator; + fileManager: FileManager; + config: DelegationProcessConfig; + logger: Logger; + }): Promise => { + const templateFilePath = path.resolve( + dirname, + "..", + "resources/templates", + "delegationApprovedTemplate.html" + ); + const documentCreatedAt = new Date(); + const todayDate = dateAtRomeZone(documentCreatedAt); + const todayTime = timeAtRomeZone(documentCreatedAt); + + const documentId = generateId(); + const documentName = createDelegationDocumentName( + documentCreatedAt, + "activation" + ); + + const submissionDate = dateAtRomeZone(delegation.stamps.submission.when); + const submissionTime = timeAtRomeZone(delegation.stamps.submission.when); + + const pdfBuffer = await pdfGenerator.generate(templateFilePath, { + todayDate, + todayTime, + delegationId: delegation.id, + delegatorName: delegator.name, + delegatorCode: delegator.externalId.value, + delegateName: delegate.name, + delegateCode: delegate.externalId.value, + submitterId: delegation.stamps.submission.who, + eServiceName: eservice.name, + eServiceId: eservice.id, + submissionDate, + submissionTime, + activationDate: todayDate, + activationTime: todayTime, + activatorId: delegate.id, + }); + + const documentPath = await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: `${config.delegationDocumentPath}/${delegation.id}`, + resourceId: documentId, + name: documentName, + content: pdfBuffer, + }, + logger + ); + + return { + id: documentId, + name: documentName, + prettyName: DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME, + contentType: CONTENT_TYPE_PDF, + path: documentPath, + createdAt: documentCreatedAt, + }; + }, + createRevocationContract: async ({ + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + }: { + delegation: Delegation; + delegator: Tenant; + delegate: Tenant; + eservice: EService; + pdfGenerator: PDFGenerator; + fileManager: FileManager; + config: DelegationProcessConfig; + logger: Logger; + }): Promise => { + const templateFilePath = path.resolve( + dirname, + "..", + "resources/templates", + "delegationRevokedTemplate.html" + ); + const documentCreatedAt = new Date(); + const todayDate = dateAtRomeZone(documentCreatedAt); + const todayTime = timeAtRomeZone(documentCreatedAt); + + const documentId = generateId(); + const documentName = createDelegationDocumentName( + documentCreatedAt, + "revocation" + ); + + const pdfBuffer = await pdfGenerator.generate(templateFilePath, { + todayDate, + todayTime, + delegationId: delegation.id, + delegatorName: delegator.name, + delegatorCode: delegator.externalId.value, + delegateName: delegate.name, + delegateCode: delegate.externalId.value, + submitterId: delegation.stamps.submission.who, + eServiceName: eservice.name, + eServiceId: eservice.id, + revocationDate: todayDate, + revocationTime: todayTime, + activatorId: delegate.id, + }); + + const documentPath = await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: `${config.delegationDocumentPath}/${delegation.id}`, + resourceId: documentId, + name: documentName, + content: pdfBuffer, + }, + logger + ); + + return { + id: documentId, + name: documentName, + prettyName: DELEGATION_REVOCATION_CONTRACT_PRETTY_NAME, + contentType: CONTENT_TYPE_PDF, + path: documentPath, + createdAt: documentCreatedAt, + }; + }, +}; diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts new file mode 100644 index 0000000000..dc4ada7d38 --- /dev/null +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -0,0 +1,280 @@ +import { delegationApi } from "pagopa-interop-api-clients"; +import { + AppContext, + DB, + eventRepository, + FileManager, + Logger, + PDFGenerator, + WithLogger, +} from "pagopa-interop-commons"; +import { + CorrelationId, + Delegation, + DelegationId, + delegationEventToBinaryDataV2, + delegationKind, + EService, + delegationState, + EServiceId, + generateId, + Tenant, + TenantId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { eserviceNotFound, tenantNotFound } from "../model/domain/errors.js"; +import { + toCreateEventProducerDelegationSubmitted, + toCreateEventProducerDelegationRevoked, + toCreateEventProducerDelegationApproved, + toCreateEventProducerDelegationRejected, +} from "../model/domain/toEvent.js"; +import { config } from "../config/config.js"; +import { ReadModelService } from "./readModelService.js"; +import { + assertDelegationIsRevokable, + assertDelegationNotExists, + assertDelegatorIsIPA, + assertDelegatorIsNotDelegate, + assertEserviceExists, + assertTenantAllowedToReceiveProducerDelegation, + assertIsDelegate, + assertIsState, +} from "./validators.js"; +import { contractBuilder } from "./delegationContractBuilder.js"; +import { retrieveDelegationById } from "./delegationService.js"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function delegationProducerServiceBuilder( + dbInstance: DB, + readModelService: ReadModelService, + pdfGenerator: PDFGenerator, + fileManager: FileManager +) { + const retrieveTenantById = async (tenantId: TenantId): Promise => { + const tenant = await readModelService.getTenantById(tenantId); + if (!tenant) { + throw tenantNotFound(tenantId); + } + return tenant; + }; + + const retrieveEserviceById = async (id: EServiceId): Promise => { + const eservice = await readModelService.getEServiceById(id); + if (!eservice) { + throw eserviceNotFound(id); + } + return eservice.data; + }; + + const repository = eventRepository(dbInstance, delegationEventToBinaryDataV2); + return { + async createProducerDelegation( + delegationSeed: delegationApi.DelegationSeed, + { authData, logger, correlationId }: WithLogger + ): Promise { + const delegatorId = unsafeBrandId(authData.organizationId); + const delegateId = unsafeBrandId(delegationSeed.delegateId); + const eserviceId = unsafeBrandId(delegationSeed.eserviceId); + + logger.info( + `Creating a delegation for tenant:${delegationSeed.delegateId} by producer:${delegatorId}` + ); + + assertDelegatorIsNotDelegate(delegatorId, delegateId); + + const delegator = await retrieveTenantById(delegatorId); + const delegate = await retrieveTenantById(delegateId); + + assertTenantAllowedToReceiveProducerDelegation(delegate); + await assertDelegatorIsIPA(delegator); + await assertEserviceExists(delegatorId, eserviceId, readModelService); + await assertDelegationNotExists( + delegator, + eserviceId, + delegationKind.delegatedProducer, + readModelService + ); + + const creationDate = new Date(); + const delegation = { + id: generateId(), + delegatorId, + delegateId, + eserviceId, + createdAt: creationDate, + submittedAt: creationDate, + state: delegationState.waitingForApproval, + kind: delegationKind.delegatedProducer, + stamps: { + submission: { + who: delegatorId, + when: creationDate, + }, + }, + }; + + await repository.createEvent( + toCreateEventProducerDelegationSubmitted(delegation, correlationId) + ); + + return delegation; + }, + async revokeProducerDelegation( + delegationId: DelegationId, + { authData, logger, correlationId }: WithLogger + ): Promise { + const delegatorId = unsafeBrandId(authData.organizationId); + logger.info( + `Revoking delegation:${delegationId} by producer:${delegatorId}` + ); + + const currentDelegation = await retrieveDelegationById( + readModelService, + delegationId + ); + assertDelegationIsRevokable(currentDelegation.data, delegatorId); + + const [delegator, delegate, eservice] = await Promise.all([ + retrieveTenantById(currentDelegation.data.delegatorId), + retrieveTenantById(currentDelegation.data.delegateId), + retrieveEserviceById(currentDelegation.data.eserviceId), + ]); + + const revocationContract = await contractBuilder.createRevocationContract( + { + delegation: currentDelegation.data, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + } + ); + + const now = new Date(); + const revokedDelegation = { + ...currentDelegation.data, + state: delegationState.revoked, + revokedAt: now, + revocationContract, + stamps: { + ...currentDelegation.data.stamps, + revocation: { + who: delegatorId, + when: now, + }, + }, + }; + + await repository.createEvent( + toCreateEventProducerDelegationRevoked( + revokedDelegation, + currentDelegation.metadata.version, + correlationId + ) + ); + + return revokedDelegation; + }, + async approveProducerDelegation( + delegateId: TenantId, + delegationId: DelegationId, + correlationId: CorrelationId, + logger: Logger + ): Promise { + const { data: delegation, metadata } = await retrieveDelegationById( + readModelService, + delegationId + ); + + const [delegator, delegate, eservice] = await Promise.all([ + retrieveTenantById(delegation.delegatorId), + retrieveTenantById(delegation.delegateId), + retrieveEserviceById(delegation.eserviceId), + ]); + + assertIsDelegate(delegation, delegateId); + assertIsState(delegationState.waitingForApproval, delegation); + + const activationContract = await contractBuilder.createActivationContract( + { + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger, + } + ); + + const now = new Date(); + + await repository.createEvent( + toCreateEventProducerDelegationApproved( + { + data: { + ...delegation, + state: delegationState.active, + approvedAt: now, + activationContract, + stamps: { + ...delegation.stamps, + activation: { + who: delegateId, + when: now, + }, + }, + }, + metadata, + }, + correlationId + ) + ); + }, + async rejectProducerDelegation( + delegateId: TenantId, + delegationId: DelegationId, + correlationId: CorrelationId, + rejectionReason: string + ): Promise { + const { data: delegation, metadata } = await retrieveDelegationById( + readModelService, + delegationId + ); + + assertIsDelegate(delegation, delegateId); + assertIsState(delegationState.waitingForApproval, delegation); + + await repository.createEvent( + toCreateEventProducerDelegationRejected( + { + data: { + ...delegation, + state: delegationState.rejected, + rejectedAt: new Date(), + rejectionReason, + stamps: { + ...delegation.stamps, + rejection: { + who: delegateId, + when: new Date(), + }, + }, + }, + metadata, + }, + correlationId + ) + ); + }, + }; +} + +export type DelegationProducerService = ReturnType< + typeof delegationProducerServiceBuilder +>; diff --git a/packages/delegation-process/src/services/delegationService.ts b/packages/delegation-process/src/services/delegationService.ts new file mode 100644 index 0000000000..c5dfda5677 --- /dev/null +++ b/packages/delegation-process/src/services/delegationService.ts @@ -0,0 +1,63 @@ +import { + Delegation, + DelegationId, + DelegationKind, + DelegationState, + EServiceId, + TenantId, + WithMetadata, +} from "pagopa-interop-models"; +import { delegationNotFound } from "../model/domain/errors.js"; +import { ReadModelService } from "./readModelService.js"; + +export const retrieveDelegationById = async ( + readModelService: ReadModelService, + delegationId: DelegationId +): Promise> => { + const delegation = await readModelService.getDelegationById(delegationId); + if (!delegation?.data) { + throw delegationNotFound(delegationId); + } + return delegation; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function delegationServiceBuilder(readModelService: ReadModelService) { + return { + async getDelegationById(delegationId: DelegationId): Promise { + const delegation = await retrieveDelegationById( + readModelService, + delegationId + ); + return delegation.data; + }, + // eslint-disable-next-line max-params + async getDelegations({ + delegateIds, + delegatorIds, + delegationStates, + eserviceIds, + kind, + offset, + limit, + }: { + delegateIds: TenantId[]; + delegatorIds: TenantId[]; + delegationStates: DelegationState[]; + eserviceIds: EServiceId[]; + kind: DelegationKind | undefined; + offset: number; + limit: number; + }): Promise { + return readModelService.getDelegations({ + delegateIds, + delegatorIds, + eserviceIds, + delegationStates, + kind, + offset, + limit, + }); + }, + }; +} diff --git a/packages/delegation-process/src/services/readModelService.ts b/packages/delegation-process/src/services/readModelService.ts new file mode 100644 index 0000000000..bacc278f27 --- /dev/null +++ b/packages/delegation-process/src/services/readModelService.ts @@ -0,0 +1,251 @@ +import { Filter, WithId } from "mongodb"; +import { + EServiceCollection, + ReadModelFilter, + ReadModelRepository, +} from "pagopa-interop-commons"; +import { + Delegation, + DelegationId, + DelegationKind, + DelegationState, + EService, + EServiceId, + EServiceReadModel, + genericInternalError, + Tenant, + TenantId, + WithMetadata, +} from "pagopa-interop-models"; +import { z } from "zod"; +import { GetDelegationsFilters } from "../model/domain/models.js"; + +const toReadModelFilter = ( + filters: GetDelegationsFilters +): ReadModelFilter => { + const { delegateId, delegatorId, eserviceId, delegationKind, states } = + filters; + + const delegatorIdFilter = delegatorId + ? { + "data.delegatorId": { $eq: delegatorId }, + } + : {}; + const delegateIdFilter = delegateId + ? { + "data.delegateId": { $eq: delegateId }, + } + : {}; + const eserviceIdFilter = eserviceId + ? { + "data.eserviceId": { $eq: eserviceId }, + } + : {}; + const delegationKindFilter = delegationKind + ? { + "data.kind": { $eq: delegationKind }, + } + : {}; + const stateFilter = + states && states.length > 0 + ? { + "data.state": { $in: states }, + } + : {}; + + return { + ...delegatorIdFilter, + ...delegateIdFilter, + ...eserviceIdFilter, + ...delegationKindFilter, + ...stateFilter, + }; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function readModelServiceBuilder( + readModelRepository: ReadModelRepository +) { + const delegations = readModelRepository.delegations; + const eservices = readModelRepository.eservices; + const tenants = readModelRepository.tenants; + + return { + async getEService( + eservices: EServiceCollection, + filter: Filter>> + ): Promise | undefined> { + const data = await eservices.findOne(filter, { + projection: { data: true, metadata: true }, + }); + if (!data) { + return undefined; + } else { + const result = z + .object({ + metadata: z.object({ version: z.number() }), + data: EService, + }) + .safeParse(data); + if (!result.success) { + throw genericInternalError( + `Unable to parse eService item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return { + data: result.data.data, + metadata: { version: result.data.metadata.version }, + }; + } + }, + async getDelegationById( + id: DelegationId + ): Promise | undefined> { + const data = await delegations.findOne( + { "data.id": id }, + { + projection: { data: true, metadata: true }, + } + ); + if (!data) { + return undefined; + } + + const result = Delegation.safeParse(data.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return data; + }, + async findDelegations( + filters: GetDelegationsFilters + ): Promise { + const results = await delegations + .aggregate([{ $match: toReadModelFilter(filters) }], { + allowDiskUse: true, + }) + .toArray(); + + if (!results) { + return []; + } + + return results.map((res) => { + const result = Delegation.safeParse(res.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(res)} ` + ); + } + return result.data; + }); + }, + async getEServiceById( + id: EServiceId + ): Promise | undefined> { + return this.getEService(eservices, { "data.id": id }); + }, + async createDelegation(delegation: Delegation): Promise { + await delegations.insertOne({ + data: delegation, + metadata: { version: 0 }, + }); + }, + async getTenantById(tenantId: string): Promise { + const data = await tenants.findOne( + { "data.id": tenantId }, + { projection: { data: true } } + ); + + if (data) { + const result = Tenant.safeParse(data.data); + + if (!result.success) { + throw genericInternalError( + `Unable to parse tenant item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + + return result.data; + } + return undefined; + }, + async getDelegations({ + delegateIds, + delegatorIds, + eserviceIds, + delegationStates, + kind, + offset, + limit, + }: { + delegateIds: TenantId[]; + delegatorIds: TenantId[]; + eserviceIds: EServiceId[]; + delegationStates: DelegationState[]; + kind: DelegationKind | undefined; + offset: number; + limit: number; + }): Promise { + const aggregationPipeline = [ + { + $match: { + ...ReadModelRepository.arrayToFilter(delegateIds, { + "data.delegateId": { $in: delegateIds }, + }), + ...ReadModelRepository.arrayToFilter(delegatorIds, { + "data.delegatorId": { $in: delegatorIds }, + }), + ...ReadModelRepository.arrayToFilter(eserviceIds, { + "data.eserviceId": { $in: eserviceIds }, + }), + ...ReadModelRepository.arrayToFilter(delegationStates, { + "data.state": { $in: delegationStates }, + }), + ...(kind && { + "data.kind": kind, + }), + } satisfies ReadModelFilter, + }, + { + $project: { + data: 1, + }, + }, + ]; + + const aggregationWithOffsetLimit = [ + ...aggregationPipeline, + { $skip: offset }, + { $limit: limit }, + ]; + + const data = await delegations + .aggregate(aggregationWithOffsetLimit, { allowDiskUse: true }) + .toArray(); + const result = z.array(Delegation).safeParse(data.map((a) => a.data)); + + if (!result.success) { + throw genericInternalError( + `Unable to parse delegations: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + + return result.data; + }, + }; +} + +export type ReadModelService = ReturnType; diff --git a/packages/delegation-process/src/services/validators.ts b/packages/delegation-process/src/services/validators.ts new file mode 100644 index 0000000000..c5a6437d4a --- /dev/null +++ b/packages/delegation-process/src/services/validators.ts @@ -0,0 +1,144 @@ +import { + Delegation, + DelegationKind, + DelegationState, + delegationState, + EServiceId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + Tenant, + TenantId, +} from "pagopa-interop-models"; +import { + delegationAlreadyExists, + delegationNotRevokable, + delegatorAndDelegateSameIdError, + delegatorNotAllowToRevoke, + differentEServiceProducer, + eserviceNotFound, + incorrectState, + invalidExternalOriginError, + operationRestrictedToDelegate, + tenantNotAllowedToDelegation, + tenantNotFound, +} from "../model/domain/errors.js"; +import { ReadModelService } from "./readModelService.js"; + +/* ========= STATES ========= */ +export const delegationNotActivableStates: DelegationState[] = [ + delegationState.rejected, + delegationState.revoked, +]; + +export const activeDelegationStates: DelegationState[] = [ + delegationState.waitingForApproval, + delegationState.active, +]; + +export const assertEserviceExists = async ( + delegatorId: TenantId, + eserviceId: EServiceId, + readModelService: ReadModelService +): Promise => { + const eservice = await readModelService.getEServiceById(eserviceId); + if (!eservice) { + throw eserviceNotFound(eserviceId); + } + + if (eservice.data.producerId !== delegatorId) { + throw differentEServiceProducer(delegatorId); + } +}; + +export const assertDelegatorIsNotDelegate = ( + delegatorId: TenantId, + delegateId: TenantId +): void => { + if (delegatorId === delegateId) { + throw delegatorAndDelegateSameIdError(); + } +}; + +export const assertDelegatorIsIPA = async ( + delegator?: Tenant +): Promise => { + if (delegator?.externalId?.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { + throw invalidExternalOriginError(delegator?.externalId?.origin); + } +}; + +export const assertTenantAllowedToReceiveProducerDelegation = ( + tenant: Tenant +): void => { + const delegationFeature = tenant.features.find( + (f) => f.type === "DelegatedProducer" + ); + + if (!delegationFeature) { + throw tenantNotAllowedToDelegation(tenant.id); + } +}; + +export const assertTenantExists = async ( + tenantId: TenantId, + readModelService: ReadModelService +): Promise => { + const tenant = await readModelService.getTenantById(tenantId); + if (!tenant) { + throw tenantNotFound(tenantId); + } +}; + +export const assertDelegationIsRevokable = ( + delegation: Delegation, + expectedDelegatorId: TenantId +): void => { + if (delegation.delegatorId !== expectedDelegatorId) { + throw delegatorNotAllowToRevoke(delegation); + } + + if (!activeDelegationStates.includes(delegation.state)) { + throw delegationNotRevokable(delegation); + } +}; + +export const assertDelegationNotExists = async ( + delegator: Tenant, + eserviceId: EServiceId, + delegationKind: DelegationKind, + readModelService: ReadModelService +): Promise => { + const delegatorId = delegator.id; + + const delegations = await readModelService.findDelegations({ + delegatorId, + eserviceId, + delegationKind, + states: [delegationState.active, delegationState.waitingForApproval], + }); + + if (delegations.length > 0) { + throw delegationAlreadyExists(delegatorId, eserviceId, delegationKind); + } +}; + +export const assertIsDelegate = ( + delegation: Delegation, + delegateId: TenantId +): void => { + if (delegation.delegateId !== delegateId) { + throw operationRestrictedToDelegate(delegateId, delegation.id); + } +}; + +export const assertIsState = ( + state: DelegationState, + delegation: Delegation +): void => { + if (delegation.state !== state) { + throw incorrectState( + delegation.id, + delegation.state, + delegationState.waitingForApproval + ); + } +}; diff --git a/packages/delegation-process/src/utilites/errorMappers.ts b/packages/delegation-process/src/utilites/errorMappers.ts new file mode 100644 index 0000000000..b009d21344 --- /dev/null +++ b/packages/delegation-process/src/utilites/errorMappers.ts @@ -0,0 +1,62 @@ +/* eslint-disable sonarjs/no-identical-functions */ +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, + HTTP_STATUS_BAD_REQUEST, + HTTP_STATUS_FORBIDDEN, + HTTP_STATUS_UNAUTHORIZED, +} = constants; + +export const getDelegationByIdsrrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const createProducerDelegationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "eserviceNotFound", + "delegationAlreadyExists", + "tenantNotFound", + "invalidDelegatorAndDelegateIds", + "invalidExternalOriginId", + "tenantNotAllowedToDelegation", + () => HTTP_STATUS_BAD_REQUEST + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getDelegationErrorMapper = (error: ApiError): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const revokeDelegationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("delegationNotRevokable", () => HTTP_STATUS_FORBIDDEN) + .with("operationNotAllowOnDelegation", () => HTTP_STATUS_UNAUTHORIZED) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const approveDelegationErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("operationRestrictedToDelegate", () => HTTP_STATUS_FORBIDDEN) + .with("incorrectState", () => HTTP_STATUS_BAD_REQUEST) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const rejectDelegationErrorMapper = approveDelegationErrorMapper; diff --git a/packages/delegation-process/test/.eslintrc.json b/packages/delegation-process/test/.eslintrc.json new file mode 100644 index 0000000000..6135a5ce08 --- /dev/null +++ b/packages/delegation-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/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts new file mode 100644 index 0000000000..c88b48a689 --- /dev/null +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -0,0 +1,247 @@ +/* eslint-disable functional/no-let */ +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockTenant, + getMockEService, +} from "pagopa-interop-commons-test/index.js"; +import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { + ProducerDelegationApprovedV2, + DelegationContractId, + DelegationId, + EService, + generateId, + Tenant, + toDelegationV2, + unsafeBrandId, +} from "pagopa-interop-models"; +import { delegationState } from "pagopa-interop-models"; +import { + formatDateyyyyMMddHHmmss, + genericLogger, +} from "pagopa-interop-commons"; +import { + delegationNotFound, + operationRestrictedToDelegate, + incorrectState, +} from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { contractBuilder } from "../src/services/delegationContractBuilder.js"; +import { + addOneDelegation, + addOneTenant, + addOneEservice, + delegationProducerService, + fileManager, + readLastDelegationEvent, + pdfGenerator, + flushPDFMetadata, +} from "./utils.js"; + +describe("approve delegation", () => { + const currentExecutionTime = new Date(); + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + }); + + let delegate: Tenant; + let delegator: Tenant; + let eservice: EService; + + beforeEach(async () => { + delegate = getMockTenant(); + delegator = getMockTenant(); + eservice = getMockEService(); + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneEservice(eservice); + }); + + it("should approve delegation if validations succeed", async () => { + const delegationId = generateId(); + + const delegation = getMockDelegationProducer({ + id: delegationId, + state: "WaitingForApproval", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + const { version } = await readLastDelegationEvent(delegation.id); + expect(version).toBe("0"); + + await delegationProducerService.approveProducerDelegation( + delegate.id, + delegation.id, + generateId(), + genericLogger + ); + + const event = await readLastDelegationEvent(delegation.id); + expect(event.version).toBe("1"); + + const { delegation: actualDelegation } = decodeProtobufPayload({ + messageType: ProducerDelegationApprovedV2, + payload: event.data, + }); + + const expectedContractFilePath = ( + await fileManager.listFiles(config.s3Bucket, genericLogger) + )[0]; + + const documentId = unsafeBrandId( + expectedContractFilePath.split("/")[2] + ); + + const expectedDelegation = { + ...toDelegationV2({ + ...delegation, + state: delegationState.active, + approvedAt: currentExecutionTime, + stamps: { + ...delegation.stamps, + activation: { + who: delegate.id, + when: currentExecutionTime, + }, + }, + activationContract: { + id: documentId, + contentType: "application/pdf", + createdAt: currentExecutionTime, + name: `${formatDateyyyyMMddHHmmss( + currentExecutionTime + )}_delegation_activation_contract.pdf`, + path: expectedContractFilePath, + prettyName: "Delega", + }, + }), + }; + expect(actualDelegation).toEqual(expectedDelegation); + + const actualContract = await fileManager.get( + config.s3Bucket, + expectedContractFilePath, + genericLogger + ); + + const { path: expectedContractPath } = + await contractBuilder.createActivationContract({ + delegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger: genericLogger, + }); + + const expectedContract = await fileManager.get( + config.s3Bucket, + expectedContractPath, + genericLogger + ); + + expect(flushPDFMetadata(actualContract, currentExecutionTime)).toEqual( + flushPDFMetadata(expectedContract, currentExecutionTime) + ); + }); + + it("should throw delegationNotFound when delegation doesn't exist", async () => { + const delegateId = getMockTenant().id; + const nonExistentDelegationId = + unsafeBrandId("non-existent-id"); + + await expect( + delegationProducerService.approveProducerDelegation( + delegateId, + nonExistentDelegationId, + generateId(), + genericLogger + ) + ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); + }); + + it("should throw operationRestrictedToDelegate when approver is not the delegate", async () => { + const wrongDelegate = getMockTenant(); + await addOneTenant(wrongDelegate); + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.approveProducerDelegation( + wrongDelegate.id, + delegation.id, + generateId(), + genericLogger + ) + ).rejects.toThrow( + operationRestrictedToDelegate(wrongDelegate.id, delegation.id) + ); + }); + + it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { + const delegation = getMockDelegationProducer({ + state: "Active", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.approveProducerDelegation( + delegate.id, + delegation.id, + generateId(), + genericLogger + ) + ).rejects.toThrow( + incorrectState( + delegation.id, + delegationState.active, + delegationState.waitingForApproval + ) + ); + }); + + it("should generete a pdf document for a delegation", async () => { + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); + const { version } = await readLastDelegationEvent(delegation.id); + expect(version).toBe("0"); + + await delegationProducerService.approveProducerDelegation( + delegate.id, + delegation.id, + unsafeBrandId("9999"), + genericLogger + ); + + const contracts = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + + const hasActivationContract = contracts.some( + (contract) => + contract.includes("activation") && contract.includes(delegation.id) + ); + + expect(hasActivationContract).toBeTruthy(); + }); +}); diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts new file mode 100644 index 0000000000..97b0cec3c2 --- /dev/null +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -0,0 +1,564 @@ +import { fail } from "assert"; +import { genericLogger } from "pagopa-interop-commons"; +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockEService, + getMockTenant, + getRandomAuthData, + randomArrayItem, +} from "pagopa-interop-commons-test"; +import { + Delegation, + DelegationId, + delegationKind, + delegationState, + ProducerDelegationSubmittedV2, + EServiceId, + generateId, + TenantId, + toDelegationV2, +} from "pagopa-interop-models"; +import { describe, expect, it, vi } from "vitest"; +import { + delegationAlreadyExists, + delegatorAndDelegateSameIdError, + differentEServiceProducer, + eserviceNotFound, + invalidExternalOriginError, + tenantNotAllowedToDelegation, + tenantNotFound, +} from "../src/model/domain/errors.js"; + +import { + activeDelegationStates, + delegationNotActivableStates, +} from "../src/services/validators.js"; +import { + addOneDelegation, + addOneEservice, + addOneTenant, + delegationProducerService, + readLastDelegationEvent, +} from "./utils.js"; + +/** + * Validates the creation of a delegation by comparing the actual delegation + * with the expected delegation. It ensures that the delegation IDs are defined + * and equal, and verifies that the last delegation event matches the expected + * delegation data. + * + * @param actualDelegation - The actual delegation object to be validated, + * typically a response from an API. + * @param expectedDelegation - The expected delegation object to compare against. + * @returns A promise that resolves to void. + * @throws Will fail if the delegation is not found in the event store. + */ +const expectedDelegationCreation = async ( + actualDelegation: Delegation, + expectedDelegation: Delegation +): Promise => { + expect(actualDelegation.id).toBeDefined(); + expect(expectedDelegation.id).toBeDefined(); + expect(actualDelegation.id).toEqual(expectedDelegation.id); + + const lastDelegationEvent = await readLastDelegationEvent( + actualDelegation.id + ); + + if (!lastDelegationEvent) { + fail("Creation fails: delegation not found in event-store"); + } + + const actualDelegationData = decodeProtobufPayload({ + messageType: ProducerDelegationSubmittedV2, + payload: lastDelegationEvent.data, + }); + + expect(actualDelegation).toMatchObject(expectedDelegation); + expect(actualDelegationData.delegation).toEqual( + toDelegationV2(expectedDelegation) + ); +}; + +describe("create delegation", () => { + it("should create a delegation if not exists", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); + + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + + const actualDelegation = + await delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedDelegation: Delegation = { + id: actualDelegation.id, + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + state: delegationState.waitingForApproval, + createdAt: currentExecutionTime, + submittedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: currentExecutionTime, + }, + }, + }; + + await expectedDelegationCreation(actualDelegation, expectedDelegation); + vi.useRealTimers(); + }); + + it("should create a delegation if already exists the same delegation in status Rejected or Revoked", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); + + const existentDelegation = { + ...getMockDelegationProducer({ + id: generateId(), + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + }), + state: randomArrayItem(delegationNotActivableStates), + }; + + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + await addOneDelegation(existentDelegation); + + const actualDelegation = + await delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedDelegation: Delegation = { + id: actualDelegation.id, + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + state: delegationState.waitingForApproval, + createdAt: currentExecutionTime, + submittedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: currentExecutionTime, + }, + }, + }; + + await expectedDelegationCreation(actualDelegation, expectedDelegation); + vi.useRealTimers(); + }); + + it("should throw an differentEServiceProducer error if requester is not Eservice producer", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(); + + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(differentEServiceProducer(delegatorId)); + + vi.useRealTimers(); + }); + + it.each(activeDelegationStates)( + "should throw an delegationAlreadyExists error when Delegation for eservice producer already exists with for same delegator, delegate and eserivce ", + async (validDelegationState) => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); + const existentValidDelegation = { + ...getMockDelegationProducer({ + id: generateId(), + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + }), + state: validDelegationState, + }; + + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneEservice(eservice); + // Add existent valid delegation for the same delegator, delegate and eservice + await addOneDelegation(existentValidDelegation); + // Add existent invalid delegation for the same delegator, delegate and eservice + await addOneDelegation({ + ...existentValidDelegation, + id: generateId(), + state: validDelegationState, + }); + + // Add another generic delegation + await addOneDelegation(getMockDelegationProducer()); + + // Add another delegation with same delegator + await addOneDelegation( + getMockDelegationProducer({ + delegatorId, + }) + ); + + // Add another delegation with same delegate + await addOneDelegation( + getMockDelegationProducer({ + delegateId: delegate.id, + }) + ); + + // Add another delegation for the same eservice + await addOneDelegation( + getMockDelegationProducer({ + eserviceId: eservice.id, + }) + ); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError( + delegationAlreadyExists( + delegatorId, + existentValidDelegation.eserviceId, + delegationKind.delegatedProducer + ) + ); + } + ); + + it("should throw an tenantNotFound error if delegated tenant not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = getMockTenant(delegatorId); + + const delegateId = generateId(); + + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantNotFound(delegateId)); + }); + + it("should throw an tenantNotFound error if delegator tenant not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + + const delegateId = generateId(); + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(delegate); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantNotFound(delegatorId)); + }); + + it("should throw an invalidDelegatorAndDelegateAreSame error if delegatorId and delegateId is the same", async () => { + const sameTenantId = generateId(); + const authData = getRandomAuthData(sameTenantId); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: sameTenantId, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(delegatorAndDelegateSameIdError()); + }); + + it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "NOT_IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(delegate); + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError( + invalidExternalOriginError(delegator.externalId.origin) + ); + }); + + it("should throw an eserviceNotFound error if Eservice not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + const eserviceId = generateId(); + const delegation = getMockDelegationProducer({ + id: generateId(), + delegatorId, + delegateId: delegate.id, + }); + + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(eserviceNotFound(eserviceId)); + }); + + it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "anythings", + }, + }; + + const delegate = getMockTenant(); + + await addOneTenant(delegate); + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantNotAllowedToDelegation(delegate.id)); + }); +}); diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts new file mode 100644 index 0000000000..8cd65334d5 --- /dev/null +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -0,0 +1,33 @@ +/* eslint-disable functional/no-let */ +import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { DelegationId, generateId } from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { delegationNotFound } from "../src/model/domain/errors.js"; +import { addOneDelegation, delegationService } from "./utils.js"; + +describe("get delegation by id", () => { + it("should get the delegation if it exists", async () => { + const delegation = getMockDelegationProducer(); + + await addOneDelegation(delegation); + + const expectedDelegation = await delegationService.getDelegationById( + delegation.id + ); + + expect(delegation).toEqual(expectedDelegation); + }); + + it("should fail with delegationNotFound", async () => { + const delegation = getMockDelegationProducer(); + + await addOneDelegation(delegation); + + const notFoundId = generateId(); + const expectedDelegation = delegationService.getDelegationById(notFoundId); + + await expect(expectedDelegation).rejects.toThrow( + delegationNotFound(notFoundId) + ); + }); +}); diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts new file mode 100644 index 0000000000..37a9b7a836 --- /dev/null +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -0,0 +1,68 @@ +/* eslint-disable functional/no-let */ +import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { describe, expect, it } from "vitest"; +import { addOneDelegation, delegationService } from "./utils.js"; + +describe("get delegations", () => { + it("should get delegations", async () => { + const delegation1 = getMockDelegationProducer({ state: "Active" }); + const delegation2 = getMockDelegationProducer(); + await addOneDelegation(delegation1); + await addOneDelegation(delegation2); + + const res1 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res1).toEqual([delegation1]); + + const res2 = await delegationService.getDelegations({ + delegateIds: [delegation2.delegateId], + delegatorIds: [], + delegationStates: [], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res2).toEqual([delegation2]); + + const res3 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Revoked"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res3).toEqual([]); + + const res4 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: undefined, + offset: 0, + limit: 50, + }); + expect(res4).toEqual([delegation1]); + + const res5 = await delegationService.getDelegations({ + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [delegation1.eserviceId], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }); + expect(res5).toEqual([delegation1]); + }); +}); diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts new file mode 100644 index 0000000000..bcc1bbe7ea --- /dev/null +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -0,0 +1,127 @@ +/* eslint-disable functional/no-let */ +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockTenant, +} from "pagopa-interop-commons-test/index.js"; +import { describe, expect, it, vi } from "vitest"; +import { + DelegationId, + ProducerDelegationRejectedV2, + generateId, + toDelegationV2, + unsafeBrandId, +} from "pagopa-interop-models"; +import { delegationState } from "pagopa-interop-models"; +import { + delegationNotFound, + operationRestrictedToDelegate, + incorrectState, +} from "../src/model/domain/errors.js"; +import { + addOneDelegation, + delegationProducerService, + readLastDelegationEvent, +} from "./utils.js"; + +describe("reject delegation", () => { + it("should reject delegation if all validations succed", async () => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); + + const delegate = getMockTenant(); + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + }); + await addOneDelegation(delegation); + + const rejectionReason = "I don't like computers, please send me a pigeon"; + + await delegationProducerService.rejectProducerDelegation( + delegate.id, + delegation.id, + generateId(), + rejectionReason + ); + + const event = await readLastDelegationEvent(delegation.id); + + const { delegation: actualDelegation } = decodeProtobufPayload({ + messageType: ProducerDelegationRejectedV2, + payload: event.data, + }); + const expectedDelegation = toDelegationV2({ + ...delegation, + state: delegationState.rejected, + rejectedAt: currentExecutionTime, + rejectionReason, + stamps: { + ...delegation.stamps, + rejection: { who: delegate.id, when: currentExecutionTime }, + }, + }); + expect(actualDelegation).toEqual(expectedDelegation); + }); + + it("should throw delegationNotFound when delegation doesn't exist", async () => { + const delegateId = getMockTenant().id; + const nonExistentDelegationId = + unsafeBrandId("non-existent-id"); + + await expect( + delegationProducerService.rejectProducerDelegation( + delegateId, + nonExistentDelegationId, + generateId(), + "" + ) + ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); + }); + + it("should throw operationRestrictedToDelegate when rejecter is not the delegate", async () => { + const delegate = getMockTenant(); + const wrongDelegate = getMockTenant(); + const delegation = getMockDelegationProducer({ + state: "WaitingForApproval", + delegateId: delegate.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.rejectProducerDelegation( + wrongDelegate.id, + delegation.id, + generateId(), + "" + ) + ).rejects.toThrow( + operationRestrictedToDelegate(wrongDelegate.id, delegation.id) + ); + }); + + it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { + const delegate = getMockTenant(); + const delegation = getMockDelegationProducer({ + state: "Active", + delegateId: delegate.id, + }); + await addOneDelegation(delegation); + + await expect( + delegationProducerService.rejectProducerDelegation( + delegate.id, + delegation.id, + generateId(), + "" + ) + ).rejects.toThrow( + incorrectState( + delegation.id, + delegationState.active, + delegationState.waitingForApproval + ) + ); + }); +}); diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts new file mode 100644 index 0000000000..ebff03f0cb --- /dev/null +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -0,0 +1,371 @@ +import { fail } from "assert"; +import { + decodeProtobufPayload, + getMockDelegationProducer, + getMockEService, + getMockTenant, + getRandomAuthData, +} from "pagopa-interop-commons-test"; +import { + Delegation, + DelegationId, + ProducerDelegationRevokedV2, + delegationState, + generateId, + TenantId, + fromDelegationV2, + EServiceId, + unsafeBrandId, + DelegationContractId, +} from "pagopa-interop-models"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { + formatDateyyyyMMddHHmmss, + genericLogger, +} from "pagopa-interop-commons"; +import { + delegationNotFound, + delegationNotRevokable, + delegatorNotAllowToRevoke, +} from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { contractBuilder } from "../src/services/delegationContractBuilder.js"; +import { + addOneDelegation, + addOneEservice, + addOneTenant, + delegationProducerService, + fileManager, + flushPDFMetadata, + pdfGenerator, + readDelegationEventByVersion, +} from "./utils.js"; + +type DelegationStateSeed = + | { + delegationData: { + state: "Rejected"; + rejectedAt: Date; + rejectionReason: string; + }; + stamps: { + rejection: { + who: TenantId; + when: Date; + }; + }; + } + | { + delegationData: { + state: "Revoked"; + revokedAt: Date; + }; + stamps: { + revocation: { + who: TenantId; + when: Date; + }; + }; + }; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { + const rejectionOrRevokeDate = new Date(); + rejectionOrRevokeDate.setMonth(new Date().getMonth() - 1); + + return [ + { + delegationData: { + state: delegationState.rejected, + rejectedAt: rejectionOrRevokeDate, + rejectionReason: "Test is a test stop", + }, + stamps: { + rejection: { + who: generateId(), + when: rejectionOrRevokeDate, + }, + }, + }, + { + delegationData: { + state: delegationState.revoked, + revokedAt: rejectionOrRevokeDate, + }, + stamps: { + revocation: { + who: generateId(), + when: rejectionOrRevokeDate, + }, + }, + }, + ]; +}; + +describe("revoke delegation", () => { + const TEST_EXECUTION_DATE = new Date(); + + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(TEST_EXECUTION_DATE); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + const notRevocableDelegationState = getNotRevocableStateSeeds(); + + it("should revoke a delegation if it exists", async () => { + const currentExecutionTime = new Date(); + const eserviceId = generateId(); + const delegatorId = generateId(); + const delegateId = generateId(); + const authData = getRandomAuthData(delegatorId); + + const delegationCreationDate = new Date(); + delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); + + const delegationActivationDate = new Date(); + delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); + + const delegate = getMockTenant(delegateId); + const delegator = getMockTenant(delegatorId); + const eservice = getMockEService(eserviceId); + + await addOneTenant(delegate); + await addOneTenant(delegator); + await addOneEservice(eservice); + + const existentDelegation: Delegation = { + ...getMockDelegationProducer({ + delegatorId, + delegateId, + }), + eserviceId, + approvedAt: delegationActivationDate, + submittedAt: delegationCreationDate, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + activation: { + who: delegateId, + when: delegationActivationDate, + }, + }, + }; + + await addOneDelegation(existentDelegation); + + const actualDelegation = + await delegationProducerService.revokeProducerDelegation( + existentDelegation.id, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedContractFilePath = ( + await fileManager.listFiles(config.s3Bucket, genericLogger) + )[0]; + + const documentId = unsafeBrandId( + expectedContractFilePath.split("/")[2] + ); + + const expectedDelegation: Delegation = { + ...existentDelegation, + state: delegationState.revoked, + revokedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + activation: { + who: delegateId, + when: delegationActivationDate, + }, + revocation: { + who: delegatorId, + when: currentExecutionTime, + }, + }, + revocationContract: { + id: documentId, + contentType: "application/pdf", + createdAt: currentExecutionTime, + name: `${formatDateyyyyMMddHHmmss( + currentExecutionTime + )}_delegation_revocation_contract.pdf`, + path: expectedContractFilePath, + prettyName: "Revoca della delega", + }, + }; + + expect(actualDelegation).toEqual(expectedDelegation); + + const actualContract = await fileManager.get( + config.s3Bucket, + expectedContractFilePath, + genericLogger + ); + + const { path: expectedContractPath } = + await contractBuilder.createActivationContract({ + delegation: actualDelegation, + delegator, + delegate, + eservice, + pdfGenerator, + fileManager, + config, + logger: genericLogger, + }); + + const expectedContract = await fileManager.get( + config.s3Bucket, + expectedContractPath, + genericLogger + ); + + expect(flushPDFMetadata(actualContract, currentExecutionTime)).toEqual( + flushPDFMetadata(expectedContract, currentExecutionTime) + ); + + const lastDelegationEvent = await readDelegationEventByVersion( + actualDelegation.id, + 1 + ); + + const delegationEventPayload = decodeProtobufPayload({ + messageType: ProducerDelegationRevokedV2, + payload: lastDelegationEvent.data, + }).delegation; + if (!delegationEventPayload) { + return fail("DelegationRevokedV2 payload not found"); + } + + const delegationFromLastEvent = fromDelegationV2(delegationEventPayload); + + expect(lastDelegationEvent.version).toBe("1"); + expect(delegationFromLastEvent).toMatchObject(expectedDelegation); + }); + + it("should throw an delegationNotFound if Delegation not exists", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegationId = generateId(); + await expect( + delegationProducerService.revokeProducerDelegation(delegationId, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + }) + ).rejects.toThrow(delegationNotFound(delegationId)); + }); + + it("should throw an delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { + const currentExecutionTime = new Date(); + + const delegatorId = generateId(); + const delegateId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegationId = generateId(); + + const delegationCreationDate = new Date(); + delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); + + const delegationApprovalDate = new Date(); + delegationApprovalDate.setMonth(currentExecutionTime.getMonth() - 1); + + const existentDelegation = { + ...getMockDelegationProducer({ + id: delegationId, + delegateId, + }), + approvedAt: delegationApprovalDate, + submittedAt: delegationCreationDate, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + approval: { + who: delegateId, + when: delegationApprovalDate, + }, + }, + }; + + await addOneDelegation(existentDelegation); + + await expect( + delegationProducerService.revokeProducerDelegation(delegationId, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + }) + ).rejects.toThrow(delegatorNotAllowToRevoke(existentDelegation)); + vi.useRealTimers(); + }); + + it.each(notRevocableDelegationState)( + "should throw an delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", + async (notRevocableDelegationState: DelegationStateSeed) => { + const currentExecutionTime = new Date(); + + const delegatorId = generateId(); + const delegateId = generateId(); + const authData = getRandomAuthData(delegatorId); + + const delegationCreationDate = new Date(); + delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); + + const delegationActivationDate = new Date(); + delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); + + const existentDelegation: Delegation = { + ...getMockDelegationProducer({ + delegatorId, + delegateId, + }), + approvedAt: delegationActivationDate, + submittedAt: delegationCreationDate, + stamps: { + submission: { + who: delegatorId, + when: delegationCreationDate, + }, + activation: { + who: delegateId, + when: delegationActivationDate, + }, + ...notRevocableDelegationState.stamps, + }, + ...notRevocableDelegationState.delegationData, + }; + + await addOneDelegation(existentDelegation); + + await expect( + delegationProducerService.revokeProducerDelegation( + existentDelegation.id, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrow(delegationNotRevokable(existentDelegation)); + } + ); +}); diff --git a/packages/delegation-process/test/tsconfig.json b/packages/delegation-process/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/delegation-process/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/delegation-process/test/utils.ts b/packages/delegation-process/test/utils.ts new file mode 100644 index 0000000000..94fbca3251 --- /dev/null +++ b/packages/delegation-process/test/utils.ts @@ -0,0 +1,137 @@ +/* eslint-disable functional/no-let */ +import { + ReadEvent, + readEventByStreamIdAndVersion, + readLastEventByStreamId, + setupTestContainersVitest, + StoredEvent, + writeInEventstore, + writeInReadmodel, +} from "pagopa-interop-commons-test"; +import { + Delegation, + DelegationEvent, + DelegationId, + EService, + Tenant, + toDelegationV2, + toReadModelEService, + toReadModelTenant, +} from "pagopa-interop-models"; +import { afterAll, afterEach, inject, vi } from "vitest"; +import { + initPDFGenerator, + launchPuppeteerBrowser, +} from "pagopa-interop-commons"; +import puppeteer, { Browser } from "puppeteer"; +import { PDFDocument } from "pdf-lib"; +import { delegationProducerServiceBuilder } from "../src/services/delegationProducerService.js"; +import { delegationServiceBuilder } from "../src/services/delegationService.js"; +import { readModelServiceBuilder } from "../src/services/readModelService.js"; + +export const { cleanup, readModelRepository, postgresDB, fileManager } = + await setupTestContainersVitest( + inject("readModelConfig"), + inject("eventStoreConfig"), + inject("fileManagerConfig") + ); +afterEach(cleanup); + +export const delegations = readModelRepository.delegations; +export const eservices = readModelRepository.eservices; +export const tenants = readModelRepository.tenants; + +export const readModelService = readModelServiceBuilder(readModelRepository); + +const testBrowserInstance: Browser = await launchPuppeteerBrowser({ + pipe: true, +}); +const closeTestBrowserInstance = async (): Promise => + await testBrowserInstance.close(); + +afterAll(closeTestBrowserInstance); +afterAll(() => { + vi.useRealTimers(); +}); + +vi.spyOn(puppeteer, "launch").mockImplementation( + async () => testBrowserInstance +); + +export const pdfGenerator = await initPDFGenerator(); + +export const delegationProducerService = delegationProducerServiceBuilder( + postgresDB, + readModelService, + pdfGenerator, + fileManager +); + +export const delegationService = delegationServiceBuilder(readModelService); + +export const writeSubmitDelegationInEventstore = async ( + delegation: Delegation +): Promise => { + const createProducerDelegationEvent: DelegationEvent = { + type: "ProducerDelegationSubmitted", + event_version: 2, + data: { + delegation: toDelegationV2(delegation), + }, + }; + + const eventToWrite: StoredEvent = { + stream_id: delegation.id, + version: 0, + event: createProducerDelegationEvent, + }; + + await writeInEventstore(eventToWrite, "delegation", postgresDB); +}; + +export const readLastDelegationEvent = async ( + delegationId: DelegationId +): Promise> => + await readLastEventByStreamId(delegationId, "delegation", postgresDB); + +export const readDelegationEventByVersion = async ( + delegationId: DelegationId, + version: number +): Promise> => + await readEventByStreamIdAndVersion( + delegationId, + version, + "delegation", + postgresDB + ); + +export const addOneDelegation = async ( + delegation: Delegation +): Promise => { + await writeSubmitDelegationInEventstore(delegation); + await writeInReadmodel(delegation, delegations); +}; + +export const addOneTenant = async (tenant: Tenant): Promise => { + await writeInReadmodel(toReadModelTenant(tenant), tenants); +}; +export const addOneEservice = async (eservice: EService): Promise => { + await writeInReadmodel(toReadModelEService(eservice), eservices); +}; + +export const flushPDFMetadata = async ( + byteArray: Uint8Array, + currentExecutionTime: Date +): Promise => { + const pdfModified = await PDFDocument.load(byteArray); + // Remove metadata properties + pdfModified.setTitle(""); + pdfModified.setAuthor(""); + pdfModified.setSubject(""); + pdfModified.setKeywords([]); + pdfModified.setProducer(""); + pdfModified.setCreator(""); + pdfModified.setCreationDate(currentExecutionTime); + pdfModified.setModificationDate(currentExecutionTime); + return await pdfModified.save(); +}; diff --git a/packages/delegation-process/test/vitestGlobalSetup.ts b/packages/delegation-process/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..85a4c8ea41 --- /dev/null +++ b/packages/delegation-process/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test/index.js"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/delegation-process/tsconfig.check.json b/packages/delegation-process/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/delegation-process/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/delegation-process/tsconfig.json b/packages/delegation-process/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/delegation-process/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/delegation-process/vitest.config.ts b/packages/delegation-process/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/delegation-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/packages/delegation-readmodel-writer/.env b/packages/delegation-readmodel-writer/.env new file mode 100644 index 0000000000..43cdfd569a --- /dev/null +++ b/packages/delegation-readmodel-writer/.env @@ -0,0 +1,13 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="delegation" +KAFKA_GROUP_ID="delegation-group" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +DELEGATION_TOPIC="event-store.delegation.events" +READMODEL_DB_HOST="localhost" +READMODEL_DB_NAME="readmodel" +READMODEL_DB_USERNAME="root" +READMODEL_DB_PASSWORD="example" +READMODEL_DB_PORT=27017 +AWS_REGION="eu-south-1" diff --git a/packages/delegation-readmodel-writer/Dockerfile b/packages/delegation-readmodel-writer/Dockerfile new file mode 100644 index 0000000000..06f93b3fba --- /dev/null +++ b/packages/delegation-readmodel-writer/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/delegation-readmodel-writer/package.json /app/packages/delegation-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/delegation-readmodel-writer /app/packages/delegation-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/delegation-readmodel-writer/node_modules \ + package*.json packages/delegation-readmodel-writer/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/delegation-readmodel-writer/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/delegation-readmodel-writer +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/delegation-readmodel-writer/package.json b/packages/delegation-readmodel-writer/package.json new file mode 100644 index 0000000000..22bba68c81 --- /dev/null +++ b/packages/delegation-readmodel-writer/package.json @@ -0,0 +1,42 @@ +{ + "name": "pagopa-interop-delegation-readmodel-writer", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability delegation 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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "@protobuf-ts/runtime": "2.9.4", + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/delegation-readmodel-writer/src/config/config.ts b/packages/delegation-readmodel-writer/src/config/config.ts new file mode 100644 index 0000000000..31bf913b82 --- /dev/null +++ b/packages/delegation-readmodel-writer/src/config/config.ts @@ -0,0 +1,16 @@ +import { + DelegationTopicConfig, + ReadModelWriterConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const DelegationReadModelWriterConfig = ReadModelWriterConfig.and( + DelegationTopicConfig +); + +export type DelegationReadModelWriterConfig = z.infer< + typeof DelegationReadModelWriterConfig +>; + +export const config: DelegationReadModelWriterConfig = + DelegationReadModelWriterConfig.parse(process.env); diff --git a/packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts b/packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts new file mode 100644 index 0000000000..65cdc3c701 --- /dev/null +++ b/packages/delegation-readmodel-writer/src/delegationConsumerServiceV2.ts @@ -0,0 +1,38 @@ +import { DelegationCollection } from "pagopa-interop-commons"; +import { + DelegationEventEnvelopeV2, + fromDelegationV2, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; + +export async function handleMessageV2( + message: DelegationEventEnvelopeV2, + delegations: DelegationCollection +): Promise { + await match(message) + .with( + { type: "ProducerDelegationApproved" }, + { type: "ProducerDelegationRejected" }, + { type: "ProducerDelegationRevoked" }, + { type: "ProducerDelegationSubmitted" }, + async (message) => { + const delegation = message.data.delegation; + await delegations.updateOne( + { + "data.id": message.stream_id, + "metadata.version": { $lte: message.version }, + }, + { + $set: { + data: delegation ? fromDelegationV2(delegation) : undefined, + metadata: { + version: message.version, + }, + }, + }, + { upsert: true } + ); + } + ) + .exhaustive(); +} diff --git a/packages/delegation-readmodel-writer/src/index.ts b/packages/delegation-readmodel-writer/src/index.ts new file mode 100644 index 0000000000..7044e3e07c --- /dev/null +++ b/packages/delegation-readmodel-writer/src/index.ts @@ -0,0 +1,45 @@ +import { EachMessagePayload } from "kafkajs"; +import { + logger, + ReadModelRepository, + decodeKafkaMessage, +} from "pagopa-interop-commons"; +import { runConsumer } from "kafka-iam-auth"; +import { + CorrelationId, + DelegationEvent, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { handleMessageV2 } from "./delegationConsumerServiceV2.js"; +import { config } from "./config/config.js"; + +const { delegations } = ReadModelRepository.init(config); + +async function processMessage({ + message, + partition, +}: EachMessagePayload): Promise { + const decodedMessage = decodeKafkaMessage(message, DelegationEvent); + + const loggerInstance = logger({ + serviceName: "delegation-readmodel-writer", + eventType: decodedMessage.type, + eventVersion: decodedMessage.event_version, + streamId: decodedMessage.stream_id, + correlationId: decodedMessage.correlation_id + ? unsafeBrandId(decodedMessage.correlation_id) + : generateId(), + }); + + await match(decodedMessage) + .with({ event_version: 2 }, (msg) => handleMessageV2(msg, delegations)) + .exhaustive(); + + loggerInstance.info( + `Read model was updated. Partition number: ${partition}. Offset: ${message.offset}` + ); +} + +await runConsumer(config, [config.delegationTopic], processMessage); diff --git a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts new file mode 100644 index 0000000000..48348a453f --- /dev/null +++ b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts @@ -0,0 +1,121 @@ +import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { + DelegationEventEnvelopeV2, + toDelegationV2, + ProducerDelegationApprovedV2, + ProducerDelegationRejectedV2, + ProducerDelegationRevokedV2, + ProducerDelegationSubmittedV2, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { handleMessageV2 } from "../src/delegationConsumerServiceV2.js"; +import { delegations } from "./utils.js"; + +describe("Events V2", async () => { + const mockDelegation = getMockDelegationProducer(); + const mockMessage: DelegationEventEnvelopeV2 = { + event_version: 2, + stream_id: mockDelegation.id, + version: 1, + sequence_num: 1, + log_date: new Date(), + type: "ProducerDelegationApproved", + data: {}, + }; + + it("ProducerDelegationApproved", async () => { + const payload: ProducerDelegationApprovedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationApproved", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); + + it("ProducerDelegationRejected", async () => { + const payload: ProducerDelegationRejectedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationRejected", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); + + it("ProducerDelegationRevoked", async () => { + const payload: ProducerDelegationRevokedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationRevoked", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); + + it("ProducerDelegationSubmitted", async () => { + const payload: ProducerDelegationSubmittedV2 = { + delegation: toDelegationV2(mockDelegation), + }; + + const message: DelegationEventEnvelopeV2 = { + ...mockMessage, + type: "ProducerDelegationSubmitted", + data: payload, + }; + + await handleMessageV2(message, delegations); + + const retrievedDelegation = await delegations.findOne({ + "data.id": mockDelegation.id, + }); + + expect(retrievedDelegation?.data).toEqual(mockDelegation); + + expect(retrievedDelegation?.metadata).toEqual({ + version: 1, + }); + }); +}); diff --git a/packages/delegation-readmodel-writer/test/tsconfig.json b/packages/delegation-readmodel-writer/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/delegation-readmodel-writer/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/delegation-readmodel-writer/test/utils.ts b/packages/delegation-readmodel-writer/test/utils.ts new file mode 100644 index 0000000000..adf10b846f --- /dev/null +++ b/packages/delegation-readmodel-writer/test/utils.ts @@ -0,0 +1,10 @@ +import { setupTestContainersVitest } from "pagopa-interop-commons-test"; +import { inject, afterEach } from "vitest"; + +export const { cleanup, readModelRepository } = await setupTestContainersVitest( + inject("readModelConfig") +); + +afterEach(cleanup); + +export const delegations = readModelRepository.delegations; diff --git a/packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts b/packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/delegation-readmodel-writer/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/delegation-readmodel-writer/tsconfig.check.json b/packages/delegation-readmodel-writer/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/delegation-readmodel-writer/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/delegation-readmodel-writer/tsconfig.json b/packages/delegation-readmodel-writer/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/delegation-readmodel-writer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/delegation-readmodel-writer/vitest.config.ts b/packages/delegation-readmodel-writer/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/delegation-readmodel-writer/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/packages/models/proto/v2/delegation/delegation.proto b/packages/models/proto/v2/delegation/delegation.proto new file mode 100644 index 0000000000..abacf41bd4 --- /dev/null +++ b/packages/models/proto/v2/delegation/delegation.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package delegation.v2; + +message DelegationV2 { + string id = 1; + string delegatorId = 2; + string delegateId = 3; + string eserviceId = 4; + int64 createdAt = 5; + int64 submittedAt = 6; + optional int64 approvedAt = 7; + optional int64 rejectedAt = 8; + optional string rejectionReason = 9; + optional int64 revokedAt = 10; + DelegationStateV2 state = 11; + DelegationKindV2 kind = 12; + DelegationStampsV2 stamps = 13; + optional DelegationContractDocumentV2 activationContract = 14; + optional DelegationContractDocumentV2 revocationContract = 15; +} + +message DelegationContractDocumentV2 { + string id = 1; + string name = 2; + string prettyName = 3; + string contentType = 4; + string path = 5; + int64 createdAt = 6; +} + +message DelegationStampV2 { + string who = 1; + int64 when = 2; +} + +message DelegationStampsV2 { + DelegationStampV2 submission = 1; + optional DelegationStampV2 activation = 2; + optional DelegationStampV2 rejection = 3; + optional DelegationStampV2 revocation = 4; +} + +enum DelegationStateV2 { + WAITING_FOR_APPROVAL= 0; + ACTIVE= 1; + REJECTED= 2; + REVOKED= 3; +} + +enum DelegationKindV2 { + DELEGATED_PRODUCER = 0; + DELEGATED_CONSUMER = 1; +} \ No newline at end of file diff --git a/packages/models/proto/v2/delegation/events.proto b/packages/models/proto/v2/delegation/events.proto new file mode 100644 index 0000000000..ef551774ff --- /dev/null +++ b/packages/models/proto/v2/delegation/events.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package delegation.v2; + +import "v2/delegation/delegation.proto"; + +message ProducerDelegationSubmittedV2 { + DelegationV2 delegation = 1; +} + +message ProducerDelegationApprovedV2 { + DelegationV2 delegation = 1; +} + +message ProducerDelegationRejectedV2 { + DelegationV2 delegation = 1; +} + +message ProducerDelegationRevokedV2 { + DelegationV2 delegation = 1; +} diff --git a/packages/models/proto/v2/tenant/events.proto b/packages/models/proto/v2/tenant/events.proto index 370e390292..a45cfe7e38 100644 --- a/packages/models/proto/v2/tenant/events.proto +++ b/packages/models/proto/v2/tenant/events.proto @@ -75,3 +75,7 @@ message TenantMailDeletedV2 { message MaintenanceTenantPromotedToCertifierV2{ TenantV2 tenant = 1; } + +message TenantDelegatedProducerFeatureAddedV2 { + TenantV2 tenant = 1; +} diff --git a/packages/models/proto/v2/tenant/tenant.proto b/packages/models/proto/v2/tenant/tenant.proto index 5cb16a63ae..75aaf6d2bb 100644 --- a/packages/models/proto/v2/tenant/tenant.proto +++ b/packages/models/proto/v2/tenant/tenant.proto @@ -46,6 +46,7 @@ enum TenantUnitTypeV2 { message TenantFeatureV2 { oneof sealed_value { CertifierV2 certifier = 1; + DelegatedProducerV2 delegatedProducer = 2; } } @@ -53,6 +54,10 @@ message CertifierV2 { string certifierId = 1; } +message DelegatedProducerV2 { + int64 availabilityTimestamp = 1; +} + message TenantVerifierV2 { string id = 1; int64 verificationDate = 2; diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 84d0dd1708..3da409a6e9 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -74,6 +74,14 @@ export type SelfcareId = z.infer; export const ProducerKeychainId = z.string().uuid().brand("ProducerKeychainId"); export type ProducerKeychainId = z.infer; +export const DelegationId = z.string().uuid().brand("DelegationId"); +export type DelegationId = z.infer; + +export const DelegationContractId = z + .string() + .uuid() + .brand("DelegationContractId"); +export type DelegationContractId = z.infer; export const PlatformStatesEServiceDescriptorPK = z .string() .brand(`ESERVICEDESCRIPTOR#eServiceId#descriptorId`); @@ -152,6 +160,8 @@ type IDS = | UserId | SelfcareId | ProducerKeychainId + | DelegationId + | DelegationContractId | PlatformStatesEServiceDescriptorPK | PlatformStatesAgreementPK | PlatformStatesPurposePK diff --git a/packages/models/src/constants.ts b/packages/models/src/constants.ts new file mode 100644 index 0000000000..7f774481c2 --- /dev/null +++ b/packages/models/src/constants.ts @@ -0,0 +1,4 @@ +export const PUBLIC_ADMINISTRATIONS_IDENTIFIER = "IPA"; +export const CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS = "SAG"; +export const PUBLIC_SERVICES_MANAGERS = "L37"; +export const SCP = "PDND_INFOCAMERE-SCP"; diff --git a/packages/models/src/delegation/delegation.ts b/packages/models/src/delegation/delegation.ts new file mode 100644 index 0000000000..f9cc0dd3d6 --- /dev/null +++ b/packages/models/src/delegation/delegation.ts @@ -0,0 +1,75 @@ +import z from "zod"; +import { + DelegationContractId, + DelegationId, + EServiceId, + TenantId, +} from "../brandedIds.js"; + +export const delegationKind = { + delegatedConsumer: "DelegatedConsumer", + delegatedProducer: "DelegatedProducer", +} as const; +export const DelegationKind = z.enum([ + Object.values(delegationKind)[0], + ...Object.values(delegationKind).slice(1), +]); +export type DelegationKind = z.infer; + +export const delegationState = { + waitingForApproval: "WaitingForApproval", + active: "Active", + rejected: "Rejected", + revoked: "Revoked", +} as const; + +export const DelegationState = z.enum([ + Object.values(delegationState)[0], + ...Object.values(delegationState).slice(1), +]); +export type DelegationState = z.infer; + +export const DelegationContractDocument = z.object({ + id: DelegationContractId, + name: z.string(), + prettyName: z.string(), + contentType: z.string(), + path: z.string(), + createdAt: z.coerce.date(), +}); +export type DelegationContractDocument = z.infer< + typeof DelegationContractDocument +>; + +export const DelegationStamp = z.object({ + who: TenantId, + when: z.coerce.date(), +}); +export type DelegationStamp = z.infer; + +export const DelegationStamps = z.object({ + submission: DelegationStamp, + activation: DelegationStamp.optional(), + rejection: DelegationStamp.optional(), + revocation: DelegationStamp.optional(), +}); +export type DelegationStamps = z.infer; + +export const Delegation = z.object({ + id: DelegationId, + delegatorId: TenantId, + delegateId: TenantId, + eserviceId: EServiceId, + createdAt: z.coerce.date(), + submittedAt: z.coerce.date(), + approvedAt: z.coerce.date().optional(), + rejectedAt: z.coerce.date().optional(), + rejectionReason: z.string().optional(), + revokedAt: z.coerce.date().optional(), + state: DelegationState, + kind: DelegationKind, + activationContract: DelegationContractDocument.optional(), + revocationContract: DelegationContractDocument.optional(), + stamps: DelegationStamps, +}); +export type Delegation = z.infer; diff --git a/packages/models/src/delegation/delegationEvents.ts b/packages/models/src/delegation/delegationEvents.ts new file mode 100644 index 0000000000..89206416b6 --- /dev/null +++ b/packages/models/src/delegation/delegationEvents.ts @@ -0,0 +1,85 @@ +import { z } from "zod"; +import { match } from "ts-pattern"; + +import { + ProducerDelegationSubmittedV2, + ProducerDelegationApprovedV2, + ProducerDelegationRejectedV2, + ProducerDelegationRevokedV2, +} from "../gen/v2/delegation/events.js"; +import { protobufDecoder } from "../protobuf/protobuf.js"; +import { EventEnvelope } from "../events/events.js"; + +export const DelegationEventV2 = z.discriminatedUnion("type", [ + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationSubmitted"), + data: protobufDecoder(ProducerDelegationSubmittedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationApproved"), + data: protobufDecoder(ProducerDelegationApprovedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationRejected"), + data: protobufDecoder(ProducerDelegationRejectedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("ProducerDelegationRevoked"), + data: protobufDecoder(ProducerDelegationRevokedV2), + }), +]); + +export type DelegationEventV2 = z.infer; + +export function delegationEventToBinaryDataV2( + event: DelegationEventV2 +): Uint8Array { + return match(event) + .with({ type: "ProducerDelegationSubmitted" }, ({ data }) => + ProducerDelegationSubmittedV2.toBinary(data) + ) + .with({ type: "ProducerDelegationApproved" }, ({ data }) => + ProducerDelegationApprovedV2.toBinary(data) + ) + .with({ type: "ProducerDelegationRejected" }, ({ data }) => + ProducerDelegationRejectedV2.toBinary(data) + ) + .with({ type: "ProducerDelegationRevoked" }, ({ data }) => + ProducerDelegationRevokedV2.toBinary(data) + ) + .exhaustive(); +} + +const eventV2 = z + .object({ + event_version: z.literal(2), + }) + .passthrough(); + +export const DelegationEvent = z + .discriminatedUnion("event_version", [eventV2]) + .transform((obj, ctx) => { + const res = match(obj) + .with({ event_version: 2 }, () => DelegationEventV2.safeParse(obj)) + .exhaustive(); + + if (!res.success) { + res.error.issues.forEach(ctx.addIssue); + return z.NEVER; + } + return res.data; + }); + +export type DelegationEvent = z.infer; + +export const DelegationEventEnvelopeV2 = EventEnvelope(DelegationEventV2); +export type DelegationEventEnvelopeV2 = z.infer< + typeof DelegationEventEnvelopeV2 +>; + +export const DelegationEventEnvelope = EventEnvelope(DelegationEvent); +export type DelegationEventEnvelope = z.infer; diff --git a/packages/models/src/delegation/protobufConverterFromV2.ts b/packages/models/src/delegation/protobufConverterFromV2.ts new file mode 100644 index 0000000000..1100eebe42 --- /dev/null +++ b/packages/models/src/delegation/protobufConverterFromV2.ts @@ -0,0 +1,125 @@ +import { unsafeBrandId } from "../brandedIds.js"; +import { genericError } from "../errors.js"; +import { + DelegationContractDocumentV2, + DelegationKindV2, + DelegationStampsV2, + DelegationStampV2, + DelegationStateV2, + DelegationV2, +} from "../gen/v2/delegation/delegation.js"; +import { bigIntToDate } from "../utils.js"; +import { + Delegation, + DelegationContractDocument, + delegationKind, + DelegationKind, + DelegationStamp, + DelegationStamps, + DelegationState, + delegationState, +} from "./delegation.js"; + +export const fromDelegationStateV2 = ( + input: DelegationStateV2 +): DelegationState => { + switch (input) { + case DelegationStateV2.ACTIVE: + return delegationState.active; + case DelegationStateV2.REJECTED: + return delegationState.rejected; + case DelegationStateV2.REVOKED: + return delegationState.revoked; + case DelegationStateV2.WAITING_FOR_APPROVAL: + return delegationState.waitingForApproval; + } +}; + +export const fromDelegationKindV2 = ( + input: DelegationKindV2 +): DelegationKind => { + switch (input) { + case DelegationKindV2.DELEGATED_CONSUMER: + return delegationKind.delegatedConsumer; + case DelegationKindV2.DELEGATED_PRODUCER: + return delegationKind.delegatedProducer; + } +}; + +export const fromDelegationContractDocumentV2 = ( + input: DelegationContractDocumentV2 +): DelegationContractDocument => ({ + id: unsafeBrandId(input.id), + name: input.name, + prettyName: input.prettyName, + contentType: input.contentType, + path: input.path, + createdAt: bigIntToDate(input.createdAt), +}); + +export function fromDelegationStampV2( + input: DelegationStampV2 +): DelegationStamp; +export function fromDelegationStampV2( + input: DelegationStampV2 | undefined +): DelegationStamp | undefined { + return input + ? { + when: bigIntToDate(input.when), + who: unsafeBrandId(input.who), + } + : undefined; +} + +export const fromDelegationStampsV2 = ( + input: DelegationStampsV2 +): DelegationStamps => { + if (!input.submission) { + throw genericError( + `Error while deserializing DelegationStampsV2: missing submission stamp ` + ); + } + + return { + submission: fromDelegationStampV2(input.submission), + activation: input.activation + ? fromDelegationStampV2(input.activation) + : undefined, + rejection: input.rejection + ? fromDelegationStampV2(input.rejection) + : undefined, + revocation: input.revocation + ? fromDelegationStampV2(input.revocation) + : undefined, + }; +}; + +export const fromDelegationV2 = (input: DelegationV2): Delegation => { + if (!input.stamps) { + throw genericError( + `Error while deserializing DelegationV2 (${input.id}): missing stamps ` + ); + } + + return { + id: unsafeBrandId(input.id), + delegatorId: unsafeBrandId(input.delegatorId), + delegateId: unsafeBrandId(input.delegateId), + eserviceId: unsafeBrandId(input.eserviceId), + createdAt: bigIntToDate(input.createdAt), + submittedAt: bigIntToDate(input.submittedAt), + approvedAt: bigIntToDate(input.approvedAt), + rejectedAt: bigIntToDate(input.rejectedAt), + rejectionReason: input.rejectionReason, + revokedAt: bigIntToDate(input.revokedAt), + state: fromDelegationStateV2(input.state), + kind: fromDelegationKindV2(input.kind), + activationContract: + input.activationContract && + fromDelegationContractDocumentV2(input.activationContract), + revocationContract: + input.revocationContract && + fromDelegationContractDocumentV2(input.revocationContract), + stamps: fromDelegationStampsV2(input.stamps), + }; +}; diff --git a/packages/models/src/delegation/protobufConverterToV2.ts b/packages/models/src/delegation/protobufConverterToV2.ts new file mode 100644 index 0000000000..71872e1ba0 --- /dev/null +++ b/packages/models/src/delegation/protobufConverterToV2.ts @@ -0,0 +1,87 @@ +import { match } from "ts-pattern"; +import { + DelegationV2, + DelegationStampV2, + DelegationStampsV2, + DelegationStateV2, + DelegationKindV2, + DelegationContractDocumentV2, +} from "../gen/v2/delegation/delegation.js"; +import { dateToBigInt } from "../utils.js"; +import { + Delegation, + DelegationKind, + delegationKind, + DelegationContractDocument, + DelegationStamp, + DelegationStamps, + DelegationState, + delegationState, +} from "./delegation.js"; + +export const toDelegationStateV2 = ( + state: DelegationState +): DelegationStateV2 => + match(state) + .with(delegationState.active, () => DelegationStateV2.ACTIVE) + .with(delegationState.rejected, () => DelegationStateV2.REJECTED) + .with(delegationState.revoked, () => DelegationStateV2.REVOKED) + .with( + delegationState.waitingForApproval, + () => DelegationStateV2.WAITING_FOR_APPROVAL + ) + .exhaustive(); + +export const toDelegationKindV2 = (kind: DelegationKind): DelegationKindV2 => + match(kind) + .with( + delegationKind.delegatedConsumer, + () => DelegationKindV2.DELEGATED_CONSUMER + ) + .with( + delegationKind.delegatedProducer, + () => DelegationKindV2.DELEGATED_PRODUCER + ) + .exhaustive(); + +export const toDelegationContractDocumentV2 = ( + contract: DelegationContractDocument +): DelegationContractDocumentV2 => ({ + ...contract, + createdAt: dateToBigInt(contract.createdAt), +}); + +export const toDelegationStampV2 = ( + stamp: DelegationStamp +): DelegationStampV2 => ({ + when: dateToBigInt(stamp.when), + who: stamp.who, +}); + +export const toDelegationStampsV2 = ( + input: DelegationStamps +): DelegationStampsV2 => ({ + submission: toDelegationStampV2(input.submission), + activation: input.activation && toDelegationStampV2(input.activation), + rejection: input.rejection && toDelegationStampV2(input.rejection), + revocation: input.revocation && toDelegationStampV2(input.revocation), +}); + +export const toDelegationV2 = (delegation: Delegation): DelegationV2 => ({ + ...delegation, + state: toDelegationStateV2(delegation.state), + kind: toDelegationKindV2(delegation.kind), + createdAt: dateToBigInt(delegation.createdAt), + submittedAt: dateToBigInt(delegation.submittedAt), + approvedAt: dateToBigInt(delegation.approvedAt), + rejectedAt: dateToBigInt(delegation.rejectedAt), + revokedAt: dateToBigInt(delegation.revokedAt), + stamps: toDelegationStampsV2(delegation.stamps), + rejectionReason: delegation.rejectionReason, + activationContract: + delegation.activationContract && + toDelegationContractDocumentV2(delegation.activationContract), + revocationContract: + delegation.revocationContract && + toDelegationContractDocumentV2(delegation.revocationContract), +}); diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 853c28d655..8e7aeee6e8 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -15,6 +15,11 @@ export * from "./attribute/attributeEvents.js"; export * from "./attribute/protobufConverterFromV1.js"; export * from "./attribute/protobufConverterToV1.js"; +export * from "./delegation/delegation.js"; +export * from "./delegation/delegationEvents.js"; +export * from "./delegation/protobufConverterFromV2.js"; +export * from "./delegation/protobufConverterToV2.js"; + export * from "./email/email.js"; export * from "./eservice/eservice.js"; @@ -69,9 +74,11 @@ export * from "./read-models/tenantReadModel.js"; export * from "./read-models/purposeReadModel.js"; export * from "./read-models/readModels.js"; export * from "./read-models/authorizationReadModel.js"; +export * from "./read-models/delegationReadModel.js"; // Utilities export * from "./brandedIds.js"; +export * from "./constants.js"; export * from "./errors.js"; export * from "./utils.js"; @@ -103,3 +110,5 @@ export * from "./gen/v2/authorization/key.js"; export * from "./gen/v2/authorization/events.js"; export * from "./gen/v2/tenant/tenant.js"; export * from "./gen/v2/tenant/events.js"; +export * from "./gen/v2/delegation/delegation.js"; +export * from "./gen/v2/delegation/events.js"; diff --git a/packages/models/src/read-models/delegationReadModel.ts b/packages/models/src/read-models/delegationReadModel.ts new file mode 100644 index 0000000000..4402fcd444 --- /dev/null +++ b/packages/models/src/read-models/delegationReadModel.ts @@ -0,0 +1,5 @@ +import { z } from "zod"; +import { Delegation } from "../delegation/delegation.js"; + +export const DelegationReadModel = Delegation; +export type DelegationReadModel = z.infer; diff --git a/packages/models/src/tenant/protobufConverterFromV2.ts b/packages/models/src/tenant/protobufConverterFromV2.ts index 5901a9c88c..e61d630bc1 100644 --- a/packages/models/src/tenant/protobufConverterFromV2.ts +++ b/packages/models/src/tenant/protobufConverterFromV2.ts @@ -20,7 +20,6 @@ import { TenantMailKind, tenantMailKind, TenantMail, - TenantFeatureCertifier, TenantVerifier, TenantRevoker, TenantAttribute, @@ -29,6 +28,7 @@ import { tenantAttributeType, TenantUnitType, tenantUnitType, + TenantFeature, } from "./tenant.js"; export const fromTenantKindV2 = (input: TenantKindV2): TenantKind => { @@ -62,16 +62,18 @@ export const fromTenantMailV2 = (input: TenantMailV2): TenantMail => ({ kind: fromTenantMailKindV2(input.kind), }); -export const fromTenantFeatureV2 = ( - input: TenantFeatureV2 -): TenantFeatureCertifier => - match( - input.sealedValue - ) +export const fromTenantFeatureV2 = (input: TenantFeatureV2): TenantFeature => + match(input.sealedValue) .with({ oneofKind: "certifier" }, ({ certifier }) => ({ type: "PersistentCertifier", certifierId: certifier.certifierId, })) + .with({ oneofKind: "delegatedProducer" }, ({ delegatedProducer }) => ({ + type: "DelegatedProducer", + availabilityTimestamp: bigIntToDate( + delegatedProducer.availabilityTimestamp + ), + })) .with({ oneofKind: undefined }, () => { throw new Error("Unable to deserialize TenantFeature"); }) diff --git a/packages/models/src/tenant/protobufConverterToV2.ts b/packages/models/src/tenant/protobufConverterToV2.ts index 66f935a786..36350a983f 100644 --- a/packages/models/src/tenant/protobufConverterToV2.ts +++ b/packages/models/src/tenant/protobufConverterToV2.ts @@ -37,6 +37,14 @@ export function toFeatureV2(feature: TenantFeature): TenantFeatureV2 { }, }, })) + .with({ type: "DelegatedProducer" }, (feature) => ({ + sealedValue: { + oneofKind: "delegatedProducer", + delegatedProducer: { + availabilityTimestamp: dateToBigInt(feature.availabilityTimestamp), + }, + }, + })) .exhaustive(); } diff --git a/packages/models/src/tenant/tenant.ts b/packages/models/src/tenant/tenant.ts index 47205c6205..02f8e93bd7 100644 --- a/packages/models/src/tenant/tenant.ts +++ b/packages/models/src/tenant/tenant.ts @@ -26,10 +26,20 @@ export const TenantFeatureCertifier = z.object({ type: z.literal("PersistentCertifier"), certifierId: z.string(), }); - export type TenantFeatureCertifier = z.infer; -export const TenantFeature = TenantFeatureCertifier; // It will be extended with other features, we will use this union to discriminate them +export const TenantFeatureDelegatedProducer = z.object({ + type: z.literal("DelegatedProducer"), + availabilityTimestamp: z.coerce.date(), +}); +export type TenantFeatureDelegatedProducer = z.infer< + typeof TenantFeatureDelegatedProducer +>; + +export const TenantFeature = z.discriminatedUnion("type", [ + TenantFeatureCertifier, + TenantFeatureDelegatedProducer, +]); export type TenantFeature = z.infer; diff --git a/packages/models/src/tenant/tenantEvents.ts b/packages/models/src/tenant/tenantEvents.ts index 960d9c7ff2..8a31948355 100644 --- a/packages/models/src/tenant/tenantEvents.ts +++ b/packages/models/src/tenant/tenantEvents.ts @@ -25,6 +25,7 @@ import { MaintenanceTenantPromotedToCertifierV2, TenantMailDeletedV2, TenantKindUpdatedV2, + TenantDelegatedProducerFeatureAddedV2, } from "../gen/v2/tenant/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; import { EventEnvelope } from "../events/events.js"; @@ -109,6 +110,9 @@ export function tenantEventToBinaryDataV2(event: TenantEventV2): Uint8Array { .with({ type: "MaintenanceTenantPromotedToCertifier" }, ({ data }) => MaintenanceTenantPromotedToCertifierV2.toBinary(data) ) + .with({ type: "TenantDelegatedProducerFeatureAdded" }, ({ data }) => + TenantDelegatedProducerFeatureAddedV2.toBinary(data) + ) .exhaustive(); } @@ -228,6 +232,11 @@ export const TenantEventV2 = z.discriminatedUnion("type", [ type: z.literal("MaintenanceTenantPromotedToCertifier"), data: protobufDecoder(MaintenanceTenantPromotedToCertifierV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("TenantDelegatedProducerFeatureAdded"), + data: protobufDecoder(TenantDelegatedProducerFeatureAddedV2), + }), ]); export type TenantEventV2 = z.infer; diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index c3e80434a4..857f575ae2 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 6e79cc66a9..7a950f251c 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.4-b", + "@pagopa/interop-outbound-models": "1.0.6-b", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts index 515f66a488..f95023456a 100644 --- a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts @@ -45,6 +45,7 @@ export function toOutboundEventV2( { type: "TenantOnboarded" }, { type: "TenantOnboardDetailsUpdated" }, { type: "MaintenanceTenantPromotedToCertifier" }, + { type: "TenantDelegatedProducerFeatureAdded" }, (msg) => ({ event_version: msg.event_version, type: msg.type, diff --git a/packages/tenant-process/src/model/domain/apiConverter.ts b/packages/tenant-process/src/model/domain/apiConverter.ts index 16ec19c944..77db01d68b 100644 --- a/packages/tenant-process/src/model/domain/apiConverter.ts +++ b/packages/tenant-process/src/model/domain/apiConverter.ts @@ -40,6 +40,11 @@ export function toApiTenantFeature( certifierId: feature.certifierId, }, })) + .with({ type: "DelegatedProducer" }, (feature) => ({ + delegatedProducer: { + availabilityTimestamp: feature.availabilityTimestamp.toJSON(), + }, + })) .exhaustive(); } diff --git a/packages/tenant-process/src/model/domain/errors.ts b/packages/tenant-process/src/model/domain/errors.ts index 4fc4a3c12e..f150139a78 100644 --- a/packages/tenant-process/src/model/domain/errors.ts +++ b/packages/tenant-process/src/model/domain/errors.ts @@ -33,6 +33,7 @@ export const errorCodes = { certifierWithExistingAttributes: "0024", attributeNotFoundInTenant: "0025", tenantNotFoundByExternalId: "0026", + tenantAlreadyHasDelegatedProducerFeature: "0027", }; export type ErrorCodes = keyof typeof errorCodes; @@ -292,3 +293,13 @@ export function attributeNotFoundInTenant( title: "Attribute not found in tenant", }); } + +export function tenantAlreadyHasDelegatedProducerFeature( + tenantId: TenantId +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} already has delegated producer feature assigned`, + code: "tenantAlreadyHasDelegatedProducerFeature", + title: "Feature already assigned", + }); +} diff --git a/packages/tenant-process/src/model/domain/toEvent.ts b/packages/tenant-process/src/model/domain/toEvent.ts index 3704cc836a..dd4fa94063 100644 --- a/packages/tenant-process/src/model/domain/toEvent.ts +++ b/packages/tenant-process/src/model/domain/toEvent.ts @@ -285,3 +285,20 @@ export const toCreateEventMaintenanceTenantPromotedToCertifier = ( }, correlationId, }); + +export const toCreateEventTenantDelegatedProducerFeatureAdded = ( + version: number, + updatedTenant: Tenant, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: updatedTenant.id, + version, + event: { + type: "TenantDelegatedProducerFeatureAdded", + event_version: 2, + data: { + tenant: toTenantV2(updatedTenant), + }, + }, + correlationId, +}); diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index ab621d3f2c..7b503babf4 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -37,6 +37,7 @@ import { internalUpsertTenantErrorMapper, m2mRevokeCertifiedAttributeErrorMapper, m2mUpsertTenantErrorMapper, + assignTenantDelegatedProducerFeatureErrorMapper, } from "../utilities/errorMappers.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../config/config.js"; @@ -468,6 +469,30 @@ const tenantsRouter = ( return res.status(errorRes.status).send(errorRes); } } + ) + .post( + "/tenants/delegatedProducer", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + try { + await tenantService.assignTenantDelegatedProducerFeature({ + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + authData: ctx.authData, + logger: ctx.logger, + }); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + assignTenantDelegatedProducerFeatureErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } ); const m2mRouter = ctx.router(tenantApi.m2mApi.api, { diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index ecea35ca68..07858091a4 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -6,6 +6,7 @@ import { WithLogger, AppContext, CreateEvent, + AuthData, } from "pagopa-interop-commons"; import { Attribute, @@ -28,6 +29,7 @@ import { TenantMail, TenantEvent, tenantMailKind, + TenantFeatureCertifier, CorrelationId, tenantKind, } from "pagopa-interop-models"; @@ -49,6 +51,7 @@ import { toCreateEventTenantVerifiedAttributeAssigned, toCreateEventMaintenanceTenantPromotedToCertifier, toCreateEventTenantVerifiedAttributeRevoked, + toCreateEventTenantDelegatedProducerFeatureAdded, } from "../model/domain/toEvent.js"; import { attributeAlreadyRevoked, @@ -80,6 +83,8 @@ import { assertRequesterAllowed, assertVerifiedAttributeOperationAllowed, retrieveCertifierId, + assertRequesterIPAOrigin, + assertDelegatedProducerFeatureNotAssigned, getTenantKind, } from "./validators.js"; import { ReadModelService } from "./readModelService.js"; @@ -1441,7 +1446,9 @@ export function tenantServiceBuilder( const tenant = await retrieveTenant(tenantId, readModelService); - const certifierFeature = tenant.data.features.find((a) => a.certifierId); + const certifierFeature = tenant.data.features.find( + (a): a is TenantFeatureCertifier => a.type === "PersistentCertifier" + ); if (certifierFeature) { if (certifierId === certifierFeature.certifierId) { @@ -1507,7 +1514,7 @@ export function tenantServiceBuilder( ); const certifierId = requesterTenant.data.features.find( - (f) => f.type === "PersistentCertifier" + (f): f is TenantFeatureCertifier => f.type === "PersistentCertifier" )?.certifierId; if (!certifierId) { @@ -1577,6 +1584,47 @@ export function tenantServiceBuilder( await repository.createEvent(attributeAssignmentEvent); } }, + async assignTenantDelegatedProducerFeature({ + organizationId, + correlationId, + authData, + logger, + }: { + organizationId: TenantId; + correlationId: CorrelationId; + authData: AuthData; + logger: Logger; + }): Promise { + logger.info( + `Assigning delegated producer feature to tenant ${organizationId}` + ); + + assertRequesterIPAOrigin(authData); + + const requesterTenant = await retrieveTenant( + organizationId, + readModelService + ); + + assertDelegatedProducerFeatureNotAssigned(requesterTenant.data); + + const updatedTenant: Tenant = { + ...requesterTenant.data, + features: [ + ...requesterTenant.data.features, + { type: "DelegatedProducer", availabilityTimestamp: new Date() }, + ], + updatedAt: new Date(), + }; + + await repository.createEvent( + toCreateEventTenantDelegatedProducerFeatureAdded( + requesterTenant.metadata.version, + updatedTenant, + correlationId + ) + ); + }, }; } diff --git a/packages/tenant-process/src/services/validators.ts b/packages/tenant-process/src/services/validators.ts index 175b95a2a5..d99263f4da 100644 --- a/packages/tenant-process/src/services/validators.ts +++ b/packages/tenant-process/src/services/validators.ts @@ -3,10 +3,14 @@ import { AgreementState, Attribute, AttributeId, + CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS, EService, ExternalId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + PUBLIC_SERVICES_MANAGERS, Tenant, TenantAttribute, + TenantFeatureCertifier, TenantId, TenantKind, TenantVerifier, @@ -15,6 +19,7 @@ import { operationForbidden, tenantAttributeType, tenantKind, + SCP, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -26,6 +31,7 @@ import { tenantIsNotACertifier, verifiedAttributeSelfVerificationNotAllowed, attributeNotFound, + tenantAlreadyHasDelegatedProducerFeature, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -118,11 +124,6 @@ export function assertExpirationDateExist( } } -export const PUBLIC_ADMINISTRATIONS_IDENTIFIER = "IPA"; -const CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS = "SAG"; -const PUBLIC_SERVICES_MANAGERS = "L37"; -export const SCP = "PDND_INFOCAMERE-SCP"; - export function getTenantKind( attributes: ExternalId[], externalId: ExternalId @@ -154,6 +155,12 @@ export async function assertRequesterAllowed( } } +export function assertRequesterIPAOrigin(authData: AuthData): void { + if (authData.externalId.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { + throw operationForbidden; + } +} + export async function assertResourceAllowed( resourceId: string, authData: AuthData @@ -240,7 +247,7 @@ export function evaluateNewSelfcareId({ export function retrieveCertifierId(tenant: Tenant): string { const certifierFeature = tenant.features.find( - (f) => f.type === "PersistentCertifier" + (f): f is TenantFeatureCertifier => f.type === "PersistentCertifier" )?.certifierId; if (!certifierFeature) { @@ -248,3 +255,11 @@ export function retrieveCertifierId(tenant: Tenant): string { } return certifierFeature; } + +export function assertDelegatedProducerFeatureNotAssigned( + tenant: Tenant +): void { + if (tenant.features.some((f) => f.type === "DelegatedProducer")) { + throw tenantAlreadyHasDelegatedProducerFeature(tenant.id); + } +} diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index 65d1892143..54aa74b988 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -229,3 +229,13 @@ export const m2mRevokeCertifiedAttributeErrorMapper = ( ) .with("tenantIsNotACertifier", () => HTTP_STATUS_FORBIDDEN) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const assignTenantDelegatedProducerFeatureErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "tenantAlreadyHasDelegatedProducerFeature", + () => HTTP_STATUS_CONFLICT + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/tenant-process/test/addCertifiedAttribute.test.ts b/packages/tenant-process/test/addCertifiedAttribute.test.ts index 447420f715..d0066a2a9f 100644 --- a/packages/tenant-process/test/addCertifiedAttribute.test.ts +++ b/packages/tenant-process/test/addCertifiedAttribute.test.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { getMockTenant } from "pagopa-interop-commons-test"; +import { + getMockTenant, + getTenantOneCertifierFeature, +} from "pagopa-interop-commons-test"; import { Tenant, Attribute, @@ -59,7 +62,7 @@ describe("addCertifiedAttribute", async () => { ...getMockAttribute(), id: unsafeBrandId(tenantAttributeSeed.id), kind: attributeKind.certified, - origin: requesterTenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(requesterTenant).certifierId, }; beforeAll(async () => { diff --git a/packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts b/packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts new file mode 100644 index 0000000000..6728565f59 --- /dev/null +++ b/packages/tenant-process/test/assignTenantDelegatedProducerFeature.test.ts @@ -0,0 +1,107 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + generateId, + Tenant, + protobufDecoder, + toTenantV2, + TenantDelegatedProducerFeatureAddedV2, + operationForbidden, +} from "pagopa-interop-models"; +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; +import { readLastEventByStreamId } from "pagopa-interop-commons-test/dist/eventStoreTestUtils.js"; +import { getMockAuthData, getMockTenant } from "pagopa-interop-commons-test"; +import { tenantAlreadyHasDelegatedProducerFeature } from "../src/model/domain/errors.js"; +import { addOneTenant, postgresDB, tenantService } from "./utils.js"; + +describe("assignTenantDelegatedProducerFeature", async () => { + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("Should correctly assign the feature", async () => { + const mockTenant = getMockTenant(); + await addOneTenant(mockTenant); + await tenantService.assignTenantDelegatedProducerFeature({ + organizationId: mockTenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }); + const writtenEvent = await readLastEventByStreamId( + mockTenant.id, + "tenant", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockTenant.id, + version: "1", + type: "TenantDelegatedProducerFeatureAdded", + event_version: 2, + }); + + const writtenPayload: TenantDelegatedProducerFeatureAddedV2 | undefined = + protobufDecoder(TenantDelegatedProducerFeatureAddedV2).parse( + writtenEvent.data + ); + + const updatedTenant: Tenant = { + ...mockTenant, + features: [ + ...mockTenant.features, + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + updatedAt: new Date(), + }; + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + }); + + it("Should throw tenantAlreadyHasDelegatedProducerFeature if the requester tenant already has the delegated producer feature", async () => { + const tenant: Tenant = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(tenant); + + expect( + tenantService.assignTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }) + ).rejects.toThrowError(tenantAlreadyHasDelegatedProducerFeature(tenant.id)); + }); + it("Should throw operationForbidden if the requester tenant is not a public administration", async () => { + const tenant = getMockTenant(); + + await addOneTenant(tenant); + + expect( + tenantService.assignTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: { + ...getMockAuthData(), + externalId: { origin: "UNKNOWN", value: "test" }, + }, + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); +}); diff --git a/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts b/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts index 7147a31e23..ccd8acdb2a 100644 --- a/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts +++ b/packages/tenant-process/test/revokeCertifiedAttributeById.test.ts @@ -22,6 +22,7 @@ import { getMockTenant, readEventByStreamIdAndVersion, getRandomAuthData, + getTenantOneCertifierFeature, } from "pagopa-interop-commons-test"; import { tenantNotFound, @@ -53,7 +54,7 @@ describe("revokeCertifiedAttributeById", async () => { const attribute: Attribute = { ...getMockAttribute(), kind: attributeKind.certified, - origin: requesterTenant.features[0].certifierId, + origin: getTenantOneCertifierFeature(requesterTenant).certifierId, }; beforeAll(async () => { diff --git a/packages/tenant-process/test/selfcareUpsertTenant.test.ts b/packages/tenant-process/test/selfcareUpsertTenant.test.ts index d8b6baa577..6f19a3da66 100644 --- a/packages/tenant-process/test/selfcareUpsertTenant.test.ts +++ b/packages/tenant-process/test/selfcareUpsertTenant.test.ts @@ -12,16 +12,14 @@ import { TenantOnboardedV2, toTenantV2, CorrelationId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + SCP, } from "pagopa-interop-models"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { tenantApi } from "pagopa-interop-api-clients"; import { getMockAuthData, getMockTenant } from "pagopa-interop-commons-test"; import { selfcareIdConflict } from "../src/model/domain/errors.js"; -import { - PUBLIC_ADMINISTRATIONS_IDENTIFIER, - SCP, -} from "../src/services/validators.js"; import { addOneTenant, readLastTenantEvent, tenantService } from "./utils.js"; describe("selfcareUpsertTenant", async () => { diff --git a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts index 03fcaed360..26780629af 100644 --- a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts +++ b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts @@ -34,6 +34,7 @@ export async function handleMessageV2( { type: "MaintenanceTenantPromotedToCertifier" }, { type: "TenantMailDeleted" }, { type: "TenantKindUpdated" }, + { type: "TenantDelegatedProducerFeatureAdded" }, async (message) => await tenants.updateOne( { diff --git a/packages/tenant-readmodel-writer/test/converterV1.ts b/packages/tenant-readmodel-writer/test/converterV1.ts index d2b76f2a78..29799a15f1 100644 --- a/packages/tenant-readmodel-writer/test/converterV1.ts +++ b/packages/tenant-readmodel-writer/test/converterV1.ts @@ -35,7 +35,9 @@ export function toFeatureV1(feature: TenantFeature): TenantFeatureV1 { }, }, })) - .exhaustive(); + .otherwise(() => { + throw new Error("Unsupported tenant feature"); + }); } export function toTenantVerifierV1(verifier: TenantVerifier): TenantVerifierV1 { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3fb2443e3b..a4d8f45bdd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -975,8 +975,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1645,6 +1645,143 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/delegation-process: + dependencies: + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8) + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + express: + specifier: 4.19.2 + version: 4.19.2 + mongodb: + specifier: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 + pagopa-interop-api-clients: + specifier: workspace:* + version: link:../api-clients + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@anatine/zod-mock': + specifier: 3.13.4 + version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + pdf-lib: + specifier: 1.17.1 + version: 1.17.1 + pg-promise: + specifier: 11.8.0 + version: 11.8.0 + prettier: + specifier: 2.8.8 + version: 2.8.8 + puppeteer: + specifier: 22.11.2 + version: 22.11.2(typescript@5.4.5) + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/delegation-readmodel-writer: + dependencies: + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/dtd-catalog-exporter: dependencies: dotenv-flow: @@ -2312,8 +2449,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2617,8 +2754,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.4-b - version: 1.0.4-b + specifier: 1.0.6-b + version: 1.0.6-b '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3974,8 +4111,14 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.4-b': - resolution: {integrity: sha512-c1kxjIBCU0gqV2s3bLIdor+fIfvGLZgML2Y26dTdY3ppoXOhJBVTWKVBu5mq8nmZvZj0KnmK7tjeD6NUJjlywg==} + '@pagopa/interop-outbound-models@1.0.6-b': + resolution: {integrity: sha512-yxGueush6nX1XqU4/WHueJwNcZ56XCNDkz44RPPXLWCJSucLrw9gKO9Mc3kUA/ocGd0mIsLYOCnuRpr42Pw7Iw==} + + '@pdf-lib/standard-fonts@1.0.0': + resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} + + '@pdf-lib/upng@1.0.1': + resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -4641,9 +4784,6 @@ packages: '@types/lodash.isequal@4.5.8': resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} - '@types/lodash@4.17.13': - resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==} - '@types/lodash@4.17.6': resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} @@ -5011,6 +5151,10 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -5669,6 +5813,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + express@4.20.0: resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} engines: {node: '>= 0.10.0'} @@ -6340,6 +6488,9 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -6617,6 +6768,9 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6667,6 +6821,9 @@ packages: path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -6680,6 +6837,9 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pdf-lib@1.17.1: + resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==} + pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -6982,6 +7142,10 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + serve-static@1.16.0: resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} engines: {node: '>= 0.8.0'} @@ -9494,7 +9658,7 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 + '@smithy/credential-provider-imds': 3.1.3 '@smithy/property-provider': 3.1.3 '@smithy/types': 3.3.0 tslib: 2.6.3 @@ -10476,12 +10640,20 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.4-b': + '@pagopa/interop-outbound-models@1.0.6-b': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 zod: 3.23.8 + '@pdf-lib/standard-fonts@1.0.0': + dependencies: + pako: 1.0.11 + + '@pdf-lib/upng@1.0.1': + dependencies: + pako: 1.0.11 + '@pkgjs/parseargs@0.11.0': optional: true @@ -11453,14 +11625,12 @@ snapshots: '@types/lodash.isempty@4.4.9': dependencies: - '@types/lodash': 4.17.13 + '@types/lodash': 4.17.6 '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - '@types/lodash@4.17.13': {} - '@types/lodash@4.17.6': {} '@types/mime@1.3.5': {} @@ -11660,6 +11830,12 @@ snapshots: axios: 1.7.4 zod: 3.23.8 + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8)': + dependencies: + '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) + express: 4.19.2 + zod: 3.23.8 + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8)': dependencies: '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -11943,6 +12119,23 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -12765,6 +12958,42 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + express@4.20.0: dependencies: accepts: 1.3.8 @@ -13474,6 +13703,8 @@ snapshots: meow@12.1.1: {} + merge-descriptors@1.0.1: {} + merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -13740,6 +13971,8 @@ snapshots: package-json-from-dist@1.0.0: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -13778,6 +14011,8 @@ snapshots: path-to-regexp@0.1.10: {} + path-to-regexp@0.1.7: {} + path-to-regexp@6.3.0: {} path-type@4.0.0: {} @@ -13786,6 +14021,13 @@ snapshots: pathval@1.1.1: {} + pdf-lib@1.17.1: + dependencies: + '@pdf-lib/standard-fonts': 1.0.0 + '@pdf-lib/upng': 1.0.1 + pako: 1.0.11 + tslib: 1.14.1 + pend@1.2.0: {} pg-cloudflare@1.1.1: @@ -14159,6 +14401,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@1.15.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + serve-static@1.16.0: dependencies: encodeurl: 1.0.2 From 59dbe0f4c7cdcfa3217a3a779dc6ea6c84c49b03 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 12 Nov 2024 17:07:36 +0100 Subject: [PATCH 025/126] Fixing TS errors in delegation routers (#1194) --- packages/delegation-process/src/app.ts | 2 +- .../src/routers/DelegationProducerRouter.ts | 5 ++--- packages/delegation-process/src/routers/DelegationRouter.ts | 5 ++--- .../src/{utilites => utilities}/errorMappers.ts | 0 4 files changed, 5 insertions(+), 7 deletions(-) rename packages/delegation-process/src/{utilites => utilities}/errorMappers.ts (100%) diff --git a/packages/delegation-process/src/app.ts b/packages/delegation-process/src/app.ts index 007a7b0706..f111eb52fa 100644 --- a/packages/delegation-process/src/app.ts +++ b/packages/delegation-process/src/app.ts @@ -11,7 +11,7 @@ import delegationProducerRouter from "./routers/DelegationProducerRouter.js"; import delegationRouter from "./routers/DelegationRouter.js"; import { config } from "./config/config.js"; -const serviceName = "delgation-process"; +const serviceName = "delegation-process"; const app = zodiosCtx.app(); diff --git a/packages/delegation-process/src/routers/DelegationProducerRouter.ts b/packages/delegation-process/src/routers/DelegationProducerRouter.ts index 8e1dd50659..be00ca4d70 100644 --- a/packages/delegation-process/src/routers/DelegationProducerRouter.ts +++ b/packages/delegation-process/src/routers/DelegationProducerRouter.ts @@ -1,4 +1,3 @@ -import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ZodiosRouter } from "@zodios/express"; import { delegationApi } from "pagopa-interop-api-clients"; import { @@ -24,7 +23,7 @@ import { revokeDelegationErrorMapper, approveDelegationErrorMapper, rejectDelegationErrorMapper, -} from "../utilites/errorMappers.js"; +} from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( ReadModelRepository.init(config) @@ -52,7 +51,7 @@ const { ADMIN_ROLE } = userRoles; const delegationProducerRouter = ( ctx: ZodiosContext -): ZodiosRouter => { +): ZodiosRouter => { const delegationProducerRouter = ctx.router(delegationApi.producerApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, }); diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 99f603305a..372da10e0a 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -1,4 +1,3 @@ -import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ZodiosRouter } from "@zodios/express"; import { delegationApi } from "pagopa-interop-api-clients"; import { @@ -19,7 +18,7 @@ import { delegationToApiDelegation, } from "../model/domain/apiConverter.js"; import { makeApiProblem } from "../model/domain/errors.js"; -import { getDelegationErrorMapper } from "../utilites/errorMappers.js"; +import { getDelegationErrorMapper } from "../utilities/errorMappers.js"; import { delegationServiceBuilder } from "../services/delegationService.js"; const readModelService = readModelServiceBuilder( @@ -33,7 +32,7 @@ const { ADMIN_ROLE, API_ROLE, SECURITY_ROLE, M2M_ROLE, SUPPORT_ROLE } = const delegationRouter = ( ctx: ZodiosContext -): ZodiosRouter => { +): ZodiosRouter => { const delegationRouter = ctx.router(delegationApi.delegationApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, }); diff --git a/packages/delegation-process/src/utilites/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts similarity index 100% rename from packages/delegation-process/src/utilites/errorMappers.ts rename to packages/delegation-process/src/utilities/errorMappers.ts From 717cdc56aa0e94e3eb43fd0b687765bc6ea71c37 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Tue, 12 Nov 2024 17:45:58 +0100 Subject: [PATCH 026/126] Small fixes and refactors in `delegation-process` (#1195) --- .../src/routers/DelegationProducerRouter.ts | 9 +- .../src/routers/DelegationRouter.ts | 37 +++--- .../src/services/delegationProducerService.ts | 33 +++--- .../src/services/delegationService.ts | 50 +++++---- .../src/utilities/errorMappers.ts | 12 +- .../test/approveProducerDelegation.test.ts | 58 +++++----- .../test/getDelegationById.test.ts | 9 +- .../test/getDelegations.test.ts | 106 ++++++++++-------- .../test/rejectProducerDelegation.test.ts | 46 +++++--- 9 files changed, 209 insertions(+), 151 deletions(-) diff --git a/packages/delegation-process/src/routers/DelegationProducerRouter.ts b/packages/delegation-process/src/routers/DelegationProducerRouter.ts index be00ca4d70..f9fbe342b3 100644 --- a/packages/delegation-process/src/routers/DelegationProducerRouter.ts +++ b/packages/delegation-process/src/routers/DelegationProducerRouter.ts @@ -123,10 +123,8 @@ const delegationProducerRouter = ( try { await delegationProducerService.approveProducerDelegation( - ctx.authData.organizationId, unsafeBrandId(delegationId), - ctx.correlationId, - ctx.logger + ctx ); return res.status(204).send(); @@ -152,10 +150,9 @@ const delegationProducerRouter = ( try { await delegationProducerService.rejectProducerDelegation( - ctx.authData.organizationId, unsafeBrandId(delegationId), - ctx.correlationId, - rejectionReason + rejectionReason, + ctx ); return res.status(204).send(); diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 372da10e0a..82aa33063b 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -18,7 +18,10 @@ import { delegationToApiDelegation, } from "../model/domain/apiConverter.js"; import { makeApiProblem } from "../model/domain/errors.js"; -import { getDelegationErrorMapper } from "../utilities/errorMappers.js"; +import { + getDelegationsErrorMapper, + getDelegationByIdErrorMapper, +} from "../utilities/errorMappers.js"; import { delegationServiceBuilder } from "../services/delegationService.js"; const readModelService = readModelServiceBuilder( @@ -60,17 +63,20 @@ const delegationRouter = ( } = req.query; try { - const delegations = await delegationService.getDelegations({ - delegateIds: delegateIds.map(unsafeBrandId), - delegatorIds: delegatorIds.map(unsafeBrandId), - delegationStates: delegationStates.map( - apiDelegationStateToDelegationState - ), - eserviceIds: eserviceIds.map(unsafeBrandId), - kind: kind && apiDelegationKindToDelegationKind(kind), - offset, - limit, - }); + const delegations = await delegationService.getDelegations( + { + delegateIds: delegateIds.map(unsafeBrandId), + delegatorIds: delegatorIds.map(unsafeBrandId), + delegationStates: delegationStates.map( + apiDelegationStateToDelegationState + ), + eserviceIds: eserviceIds.map(unsafeBrandId), + kind: kind && apiDelegationKindToDelegationKind(kind), + offset, + limit, + }, + ctx.logger + ); return res.status(200).send( delegationApi.Delegations.parse({ @@ -83,7 +89,7 @@ const delegationRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - getDelegationErrorMapper, + getDelegationsErrorMapper, ctx.logger, ctx.correlationId ); @@ -107,7 +113,8 @@ const delegationRouter = ( try { const delegation = await delegationService.getDelegationById( - unsafeBrandId(delegationId) + unsafeBrandId(delegationId), + ctx.logger ); return res @@ -120,7 +127,7 @@ const delegationRouter = ( } catch (error) { const errorRes = makeApiProblem( error, - getDelegationErrorMapper, + getDelegationByIdErrorMapper, ctx.logger, ctx.correlationId ); diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index dc4ada7d38..f323a8de45 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -4,12 +4,10 @@ import { DB, eventRepository, FileManager, - Logger, PDFGenerator, WithLogger, } from "pagopa-interop-commons"; import { - CorrelationId, Delegation, DelegationId, delegationEventToBinaryDataV2, @@ -78,7 +76,7 @@ export function delegationProducerServiceBuilder( const eserviceId = unsafeBrandId(delegationSeed.eserviceId); logger.info( - `Creating a delegation for tenant:${delegationSeed.delegateId} by producer:${delegatorId}` + `Creating a delegation for tenant ${delegationSeed.delegateId} by producer ${delegatorId}` ); assertDelegatorIsNotDelegate(delegatorId, delegateId); @@ -126,7 +124,7 @@ export function delegationProducerServiceBuilder( ): Promise { const delegatorId = unsafeBrandId(authData.organizationId); logger.info( - `Revoking delegation:${delegationId} by producer:${delegatorId}` + `Revoking delegation ${delegationId} by producer ${delegatorId}` ); const currentDelegation = await retrieveDelegationById( @@ -180,25 +178,29 @@ export function delegationProducerServiceBuilder( return revokedDelegation; }, async approveProducerDelegation( - delegateId: TenantId, delegationId: DelegationId, - correlationId: CorrelationId, - logger: Logger + { logger, correlationId, authData }: WithLogger ): Promise { + const delegateId = unsafeBrandId(authData.organizationId); + + logger.info( + `Approving delegation ${delegationId} by delegate ${delegateId}` + ); + const { data: delegation, metadata } = await retrieveDelegationById( readModelService, delegationId ); + assertIsDelegate(delegation, delegateId); + assertIsState(delegationState.waitingForApproval, delegation); + const [delegator, delegate, eservice] = await Promise.all([ retrieveTenantById(delegation.delegatorId), retrieveTenantById(delegation.delegateId), retrieveEserviceById(delegation.eserviceId), ]); - assertIsDelegate(delegation, delegateId); - assertIsState(delegationState.waitingForApproval, delegation); - const activationContract = await contractBuilder.createActivationContract( { delegation, @@ -237,11 +239,16 @@ export function delegationProducerServiceBuilder( ); }, async rejectProducerDelegation( - delegateId: TenantId, delegationId: DelegationId, - correlationId: CorrelationId, - rejectionReason: string + rejectionReason: string, + { logger, correlationId, authData }: WithLogger ): Promise { + const delegateId = unsafeBrandId(authData.organizationId); + + logger.info( + `Rejecting delegation ${delegationId} by delegate ${delegateId}` + ); + const { data: delegation, metadata } = await retrieveDelegationById( readModelService, delegationId diff --git a/packages/delegation-process/src/services/delegationService.ts b/packages/delegation-process/src/services/delegationService.ts index c5dfda5677..0b55683fb4 100644 --- a/packages/delegation-process/src/services/delegationService.ts +++ b/packages/delegation-process/src/services/delegationService.ts @@ -7,6 +7,7 @@ import { TenantId, WithMetadata, } from "pagopa-interop-models"; +import { Logger } from "pagopa-interop-commons"; import { delegationNotFound } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -24,31 +25,42 @@ export const retrieveDelegationById = async ( // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function delegationServiceBuilder(readModelService: ReadModelService) { return { - async getDelegationById(delegationId: DelegationId): Promise { + async getDelegationById( + delegationId: DelegationId, + logger: Logger + ): Promise { + logger.info(`Retrieving delegation by id ${delegationId}`); + const delegation = await retrieveDelegationById( readModelService, delegationId ); return delegation.data; }, - // eslint-disable-next-line max-params - async getDelegations({ - delegateIds, - delegatorIds, - delegationStates, - eserviceIds, - kind, - offset, - limit, - }: { - delegateIds: TenantId[]; - delegatorIds: TenantId[]; - delegationStates: DelegationState[]; - eserviceIds: EServiceId[]; - kind: DelegationKind | undefined; - offset: number; - limit: number; - }): Promise { + async getDelegations( + { + delegateIds, + delegatorIds, + delegationStates, + eserviceIds, + kind, + offset, + limit, + }: { + delegateIds: TenantId[]; + delegatorIds: TenantId[]; + delegationStates: DelegationState[]; + eserviceIds: EServiceId[]; + kind: DelegationKind | undefined; + offset: number; + limit: number; + }, + logger: Logger + ): Promise { + logger.info( + `Retrieving delegations with filters: delegateIds=${delegateIds}, delegatorIds=${delegatorIds}, delegationStates=${delegationStates}, eserviceIds=${eserviceIds}, kind=${kind}, offset=${offset}, limit=${limit}` + ); + return readModelService.getDelegations({ delegateIds, delegatorIds, diff --git a/packages/delegation-process/src/utilities/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts index b009d21344..98e2a40d3a 100644 --- a/packages/delegation-process/src/utilities/errorMappers.ts +++ b/packages/delegation-process/src/utilities/errorMappers.ts @@ -14,7 +14,12 @@ const { HTTP_STATUS_UNAUTHORIZED, } = constants; -export const getDelegationByIdsrrorMapper = ( +export const getDelegationsErrorMapper = ( + error: ApiError +): number => + match(error.code).otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const getDelegationByIdErrorMapper = ( error: ApiError ): number => match(error.code) @@ -36,11 +41,6 @@ export const createProducerDelegationErrorMapper = ( ) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); -export const getDelegationErrorMapper = (error: ApiError): number => - match(error.code) - .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) - .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); - export const revokeDelegationErrorMapper = ( error: ApiError ): number => diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index c88b48a689..dc1841a5ca 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -4,6 +4,7 @@ import { getMockDelegationProducer, getMockTenant, getMockEService, + getMockAuthData, } from "pagopa-interop-commons-test/index.js"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { @@ -73,12 +74,12 @@ describe("approve delegation", () => { const { version } = await readLastDelegationEvent(delegation.id); expect(version).toBe("0"); - await delegationProducerService.approveProducerDelegation( - delegate.id, - delegation.id, - generateId(), - genericLogger - ); + await delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }); const event = await readLastDelegationEvent(delegation.id); expect(event.version).toBe("1"); @@ -158,10 +159,13 @@ describe("approve delegation", () => { await expect( delegationProducerService.approveProducerDelegation( - delegateId, nonExistentDelegationId, - generateId(), - genericLogger + { + authData: getMockAuthData(delegateId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } ) ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); }); @@ -178,12 +182,12 @@ describe("approve delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.approveProducerDelegation( - wrongDelegate.id, - delegation.id, - generateId(), - genericLogger - ) + delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(wrongDelegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( operationRestrictedToDelegate(wrongDelegate.id, delegation.id) ); @@ -199,12 +203,12 @@ describe("approve delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.approveProducerDelegation( - delegate.id, - delegation.id, - generateId(), - genericLogger - ) + delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( incorrectState( delegation.id, @@ -225,12 +229,12 @@ describe("approve delegation", () => { const { version } = await readLastDelegationEvent(delegation.id); expect(version).toBe("0"); - await delegationProducerService.approveProducerDelegation( - delegate.id, - delegation.id, - unsafeBrandId("9999"), - genericLogger - ); + await delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }); const contracts = await fileManager.listFiles( config.s3Bucket, diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts index 8cd65334d5..ddaf026c79 100644 --- a/packages/delegation-process/test/getDelegationById.test.ts +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -2,6 +2,7 @@ import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; import { DelegationId, generateId } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { delegationNotFound } from "../src/model/domain/errors.js"; import { addOneDelegation, delegationService } from "./utils.js"; @@ -12,7 +13,8 @@ describe("get delegation by id", () => { await addOneDelegation(delegation); const expectedDelegation = await delegationService.getDelegationById( - delegation.id + delegation.id, + genericLogger ); expect(delegation).toEqual(expectedDelegation); @@ -24,7 +26,10 @@ describe("get delegation by id", () => { await addOneDelegation(delegation); const notFoundId = generateId(); - const expectedDelegation = delegationService.getDelegationById(notFoundId); + const expectedDelegation = delegationService.getDelegationById( + notFoundId, + genericLogger + ); await expect(expectedDelegation).rejects.toThrow( delegationNotFound(notFoundId) diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts index 37a9b7a836..e7ba0c8381 100644 --- a/packages/delegation-process/test/getDelegations.test.ts +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -1,6 +1,7 @@ /* eslint-disable functional/no-let */ import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegations", () => { @@ -10,59 +11,74 @@ describe("get delegations", () => { await addOneDelegation(delegation1); await addOneDelegation(delegation2); - const res1 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Active"], - eserviceIds: [], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res1 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res1).toEqual([delegation1]); - const res2 = await delegationService.getDelegations({ - delegateIds: [delegation2.delegateId], - delegatorIds: [], - delegationStates: [], - eserviceIds: [], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res2 = await delegationService.getDelegations( + { + delegateIds: [delegation2.delegateId], + delegatorIds: [], + delegationStates: [], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res2).toEqual([delegation2]); - const res3 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Revoked"], - eserviceIds: [], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res3 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Revoked"], + eserviceIds: [], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res3).toEqual([]); - const res4 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Active"], - eserviceIds: [], - kind: undefined, - offset: 0, - limit: 50, - }); + const res4 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: undefined, + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res4).toEqual([delegation1]); - const res5 = await delegationService.getDelegations({ - delegateIds: [], - delegatorIds: [], - delegationStates: ["Active"], - eserviceIds: [delegation1.eserviceId], - kind: "DelegatedProducer", - offset: 0, - limit: 50, - }); + const res5 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [delegation1.eserviceId], + kind: "DelegatedProducer", + offset: 0, + limit: 50, + }, + genericLogger + ); expect(res5).toEqual([delegation1]); }); }); diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index bcc1bbe7ea..f24401386d 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -1,6 +1,7 @@ /* eslint-disable functional/no-let */ import { decodeProtobufPayload, + getMockAuthData, getMockDelegationProducer, getMockTenant, } from "pagopa-interop-commons-test/index.js"; @@ -13,6 +14,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { delegationState } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { delegationNotFound, operationRestrictedToDelegate, @@ -40,10 +42,14 @@ describe("reject delegation", () => { const rejectionReason = "I don't like computers, please send me a pigeon"; await delegationProducerService.rejectProducerDelegation( - delegate.id, delegation.id, - generateId(), - rejectionReason + rejectionReason, + { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } ); const event = await readLastDelegationEvent(delegation.id); @@ -72,10 +78,14 @@ describe("reject delegation", () => { await expect( delegationProducerService.rejectProducerDelegation( - delegateId, nonExistentDelegationId, - generateId(), - "" + "", + { + authData: getMockAuthData(delegateId), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } ) ).rejects.toThrow(delegationNotFound(nonExistentDelegationId)); }); @@ -90,12 +100,12 @@ describe("reject delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.rejectProducerDelegation( - wrongDelegate.id, - delegation.id, - generateId(), - "" - ) + delegationProducerService.rejectProducerDelegation(delegation.id, "", { + authData: getMockAuthData(wrongDelegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( operationRestrictedToDelegate(wrongDelegate.id, delegation.id) ); @@ -110,12 +120,12 @@ describe("reject delegation", () => { await addOneDelegation(delegation); await expect( - delegationProducerService.rejectProducerDelegation( - delegate.id, - delegation.id, - generateId(), - "" - ) + delegationProducerService.rejectProducerDelegation(delegation.id, "", { + authData: getMockAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) ).rejects.toThrow( incorrectState( delegation.id, From fe5344aa326205fa61d716683d98fdf1d8726d66 Mon Sep 17 00:00:00 2001 From: Alessio Gallitano <25105748+galales@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:57:06 +0100 Subject: [PATCH 027/126] PIN-5632: Add maintenance endpoint to tenant process (#1196) --- .../agreement-outbound-writer/package.json | 4 +- packages/api-clients/open-api/tenantApi.yml | 61 + packages/catalog-outbound-writer/package.json | 4 +- .../compute-agreements-consumer/src/index.ts | 1 + packages/models/proto/v2/tenant/events.proto | 4 + packages/models/src/tenant/tenantEvents.ts | 9 + packages/purpose-outbound-writer/package.json | 4 +- packages/tenant-outbound-writer/package.json | 4 +- .../src/converters/toOutboundEventV2.ts | 1 + .../src/model/domain/toEvent.ts | 17 + .../src/routers/TenantRouter.ts | 28 + .../src/services/tenantService.ts | 44 + .../src/utilities/errorMappers.ts | 7 + .../src/tenantConsumerServiceV2.ts | 1 + .../test/consumerServiceV2.test.ts | 57 + pnpm-lock.yaml | 1862 ++++++++++------- 16 files changed, 1316 insertions(+), 792 deletions(-) diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index c414df4e13..d0bf7bbb3c 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index 4bed5af187..5cf569ea6d 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -863,6 +863,26 @@ paths: required: - currentVersion required: true + post: + tags: + - tenant + summary: Updates a tenant + operationId: maintenanceTenantUpdate + responses: + "204": + description: Tenant Updated + "404": + description: Tenant not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/MaintenanceTenantUpdatePayload" + required: true /maintenance/tenants/{tenantId}/certifier: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" @@ -1468,6 +1488,47 @@ components: type: string required: - certifierId + MaintenanceTenantUpdatePayload: + description: Maintenance payload to update tenant fields + type: object + additionalProperties: false + properties: + currentVersion: + type: number + tenant: + $ref: "#/components/schemas/MaintenanceTenantUpdate" + required: + - currentVersion + - tenant + MaintenanceTenantUpdate: + description: Tenant model + type: object + additionalProperties: false + properties: + selfcareId: + type: string + externalId: + $ref: "#/components/schemas/ExternalId" + mails: + type: array + items: + $ref: "#/components/schemas/Mail" + name: + type: string + kind: + $ref: "#/components/schemas/TenantKind" + onboardedAt: + type: string + format: date-time + subUnitType: + $ref: "#/components/schemas/TenantUnitType" + required: + - selfcareId + - externalId + - mails + - name + - kind + - onboardedAt Problem: properties: type: diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 1cd5b3c363..5e81c6a5db 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/compute-agreements-consumer/src/index.ts b/packages/compute-agreements-consumer/src/index.ts index 2fa68f8742..116ddc08b9 100644 --- a/packages/compute-agreements-consumer/src/index.ts +++ b/packages/compute-agreements-consumer/src/index.ts @@ -92,6 +92,7 @@ async function processMessage({ "TenantVerifiedAttributeExpirationUpdated", "TenantKindUpdated", "MaintenanceTenantDeleted", + "MaintenanceTenantUpdated", "TenantMailDeleted", "TenantMailAdded", "MaintenanceTenantPromotedToCertifier", diff --git a/packages/models/proto/v2/tenant/events.proto b/packages/models/proto/v2/tenant/events.proto index a45cfe7e38..70fb333ef8 100644 --- a/packages/models/proto/v2/tenant/events.proto +++ b/packages/models/proto/v2/tenant/events.proto @@ -57,6 +57,10 @@ message MaintenanceTenantDeletedV2 { TenantV2 tenant = 2; } +message MaintenanceTenantUpdatedV2 { + TenantV2 tenant = 1; +} + message TenantMailAddedV2 { string mailId = 1; TenantV2 tenant = 2; diff --git a/packages/models/src/tenant/tenantEvents.ts b/packages/models/src/tenant/tenantEvents.ts index 8a31948355..14321eb4d4 100644 --- a/packages/models/src/tenant/tenantEvents.ts +++ b/packages/models/src/tenant/tenantEvents.ts @@ -26,6 +26,7 @@ import { TenantMailDeletedV2, TenantKindUpdatedV2, TenantDelegatedProducerFeatureAddedV2, + MaintenanceTenantUpdatedV2, } from "../gen/v2/tenant/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; import { EventEnvelope } from "../events/events.js"; @@ -98,6 +99,9 @@ export function tenantEventToBinaryDataV2(event: TenantEventV2): Uint8Array { .with({ type: "MaintenanceTenantDeleted" }, ({ data }) => MaintenanceTenantDeletedV2.toBinary(data) ) + .with({ type: "MaintenanceTenantUpdated" }, ({ data }) => + MaintenanceTenantUpdatedV2.toBinary(data) + ) .with({ type: "TenantMailAdded" }, ({ data }) => TenantMailAddedV2.toBinary(data) ) @@ -212,6 +216,11 @@ export const TenantEventV2 = z.discriminatedUnion("type", [ type: z.literal("MaintenanceTenantDeleted"), data: protobufDecoder(MaintenanceTenantDeletedV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("MaintenanceTenantUpdated"), + data: protobufDecoder(MaintenanceTenantUpdatedV2), + }), z.object({ event_version: z.literal(2), type: z.literal("TenantMailAdded"), diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 857f575ae2..88741fed89 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 7a950f251c..e7577c6274 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.6-b", + "@pagopa/interop-outbound-models": "1.0.10", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} +} \ No newline at end of file diff --git a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts index f95023456a..4ba155f4d4 100644 --- a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts @@ -45,6 +45,7 @@ export function toOutboundEventV2( { type: "TenantOnboarded" }, { type: "TenantOnboardDetailsUpdated" }, { type: "MaintenanceTenantPromotedToCertifier" }, + { type: "MaintenanceTenantUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, (msg) => ({ event_version: msg.event_version, diff --git a/packages/tenant-process/src/model/domain/toEvent.ts b/packages/tenant-process/src/model/domain/toEvent.ts index dd4fa94063..5b65157807 100644 --- a/packages/tenant-process/src/model/domain/toEvent.ts +++ b/packages/tenant-process/src/model/domain/toEvent.ts @@ -174,6 +174,23 @@ export const toCreateEventMaintenanceTenantDeleted = ( correlationId, }); +export const toCreateEventMaintenanceTenantUpdated = ( + version: number, + tenant: Tenant, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: tenant.id, + version, + event: { + event_version: 2, + type: "MaintenanceTenantUpdated", + data: { + tenant: toTenantV2(tenant), + }, + }, + correlationId, +}); + export const toCreateEventTenantVerifiedAttributeAssigned = ( version: number, updatedTenant: Tenant, diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index 7b503babf4..4300013729 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -37,6 +37,7 @@ import { internalUpsertTenantErrorMapper, m2mRevokeCertifiedAttributeErrorMapper, m2mUpsertTenantErrorMapper, + maintenanceTenantUpdatedErrorMapper, assignTenantDelegatedProducerFeatureErrorMapper, } from "../utilities/errorMappers.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; @@ -442,6 +443,33 @@ const tenantsRouter = ( } } ) + .post( + "/maintenance/tenants/:tenantId", + authorizationMiddleware([MAINTENANCE_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + try { + await tenantService.maintenanceTenantUpdate( + { + tenantId: unsafeBrandId(req.params.tenantId), + version: req.body.currentVersion, + tenantUpdate: req.body.tenant, + correlationId: ctx.correlationId, + }, + ctx.logger + ); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + maintenanceTenantUpdatedErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } + ) .delete( "/tenants/:tenantId/mails/:mailId", authorizationMiddleware([ADMIN_ROLE, API_ROLE]), diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index 07858091a4..1c4cd1444c 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -51,6 +51,7 @@ import { toCreateEventTenantVerifiedAttributeAssigned, toCreateEventMaintenanceTenantPromotedToCertifier, toCreateEventTenantVerifiedAttributeRevoked, + toCreateEventMaintenanceTenantUpdated, toCreateEventTenantDelegatedProducerFeatureAdded, } from "../model/domain/toEvent.js"; import { @@ -1045,6 +1046,49 @@ export function tenantServiceBuilder( ); }, + async maintenanceTenantUpdate( + { + tenantId, + tenantUpdate, + version, + correlationId, + }: { + tenantId: TenantId; + tenantUpdate: tenantApi.MaintenanceTenantUpdate; + version: number; + correlationId: CorrelationId; + }, + logger: Logger + ): Promise { + logger.info(`Maintenance update Tenant ${tenantId}`); + + const tenant = await retrieveTenant(tenantId, readModelService); + + const convertedTenantUpdate = { + ...tenantUpdate, + mails: tenantUpdate.mails.map((mail) => ({ + ...mail, + createdAt: new Date(mail.createdAt), + })), + onboardedAt: new Date(tenantUpdate.onboardedAt), + }; + + const updatedTenant: Tenant = { + ...tenant.data, + ...convertedTenantUpdate, + subUnitType: convertedTenantUpdate.subUnitType, + updatedAt: new Date(), + }; + + await repository.createEvent( + toCreateEventMaintenanceTenantUpdated( + version, + updatedTenant, + correlationId + ) + ); + }, + async deleteTenantMailById( { tenantId, diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index 54aa74b988..fc44f5b714 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -136,6 +136,13 @@ export const maintenanceTenantDeletedErrorMapper = ( .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); +export const maintenanceTenantUpdatedErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + export const verifyVerifiedAttributeErrorMapper = ( error: ApiError ): number => diff --git a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts index 26780629af..e5637e7943 100644 --- a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts +++ b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts @@ -32,6 +32,7 @@ export async function handleMessageV2( { type: "TenantVerifiedAttributeExtensionUpdated" }, { type: "TenantMailAdded" }, { type: "MaintenanceTenantPromotedToCertifier" }, + { type: "MaintenanceTenantUpdated" }, { type: "TenantMailDeleted" }, { type: "TenantKindUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, diff --git a/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts b/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts index ee8e492078..c860c6abb4 100644 --- a/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts @@ -28,6 +28,8 @@ import { TenantKindV2, tenantKind, MaintenanceTenantDeletedV2, + tenantUnitType, + MaintenanceTenantUpdatedV2, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { handleMessageV2 } from "../src/tenantConsumerServiceV2.js"; @@ -534,4 +536,59 @@ describe("Tenant Events V2", async () => { expect(retrievedTenant).toBeNull(); }); + it("MaintenanceTenantUpdated", async () => { + const mail = getMockTenantMail(); + + const tenant: Tenant = { + ...mockTenant, + selfcareId: crypto.randomUUID(), + externalId: { + origin: "o1", + value: "v1", + }, + mails: [mail], + name: "old_name", + kind: tenantKind.GSP, + onboardedAt: new Date(), + subUnitType: tenantUnitType.AOO, + }; + + await writeInReadmodel(toReadModelTenant(tenant), tenants, 1); + + const updatedTenant: Tenant = { + ...mockTenant, + selfcareId: crypto.randomUUID(), + externalId: { + origin: "o2", + value: "v2", + }, + mails: [getMockTenantMail()], + name: "new_name", + kind: tenantKind.PA, + onboardedAt: new Date(), + subUnitType: tenantUnitType.UO, + }; + + const payload: MaintenanceTenantUpdatedV2 = { + tenant: toTenantV2(updatedTenant), + }; + + const message: TenantEventEnvelopeV2 = { + ...mockMessage, + type: "MaintenanceTenantUpdated", + data: payload, + version: 2, + }; + + await handleMessageV2(message, tenants); + + const retrievedTenant = await tenants.findOne({ + "data.id": mockTenant.id, + }); + + expect(retrievedTenant?.data).toEqual(toReadModelTenant(updatedTenant)); + expect(retrievedTenant?.metadata).toEqual({ + version: 2, + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4d8f45bdd..1885d59312 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -975,8 +975,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2449,8 +2449,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2754,8 +2754,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.6-b - version: 1.0.6-b + specifier: 1.0.10 + version: 1.0.10 '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3520,6 +3520,10 @@ packages: resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.667.0': + resolution: {integrity: sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==} + engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.310.0': resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} engines: {node: '>=14.0.0'} @@ -4111,8 +4115,8 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.6-b': - resolution: {integrity: sha512-yxGueush6nX1XqU4/WHueJwNcZ56XCNDkz44RPPXLWCJSucLrw9gKO9Mc3kUA/ocGd0mIsLYOCnuRpr42Pw7Iw==} + '@pagopa/interop-outbound-models@1.0.10': + resolution: {integrity: sha512-/TDVP8j+Q0ErpVs7MzH9LhfiEPcOzqea00um4sjQ9uuA6/Yg0G9A739bDOuaxih2wKpJ3hNPLnm9riM+YAz6Ow==} '@pdf-lib/standard-fonts@1.0.0': resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} @@ -4281,6 +4285,10 @@ packages: resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} engines: {node: '>=16.0.0'} + '@smithy/abort-controller@3.1.5': + resolution: {integrity: sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==} + engines: {node: '>=16.0.0'} + '@smithy/chunked-blob-reader-native@2.2.0': resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} @@ -4305,6 +4313,10 @@ packages: resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.9': + resolution: {integrity: sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==} + engines: {node: '>=16.0.0'} + '@smithy/core@2.2.4': resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} engines: {node: '>=16.0.0'} @@ -4313,18 +4325,22 @@ packages: resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} engines: {node: '>=16.0.0'} + '@smithy/core@2.4.8': + resolution: {integrity: sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==} + engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@2.3.0': resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} engines: {node: '>=14.0.0'} - '@smithy/credential-provider-imds@3.1.3': - resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} - engines: {node: '>=16.0.0'} - '@smithy/credential-provider-imds@3.2.0': resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.2.4': + resolution: {integrity: sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==} + engines: {node: '>=16.0.0'} + '@smithy/eventstream-codec@2.2.0': resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} @@ -4372,6 +4388,9 @@ packages: '@smithy/fetch-http-handler@3.2.4': resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + '@smithy/fetch-http-handler@3.2.9': + resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} + '@smithy/hash-blob-browser@2.2.0': resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} @@ -4386,6 +4405,10 @@ packages: resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} engines: {node: '>=16.0.0'} + '@smithy/hash-node@3.0.7': + resolution: {integrity: sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==} + engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@2.2.0': resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} engines: {node: '>=14.0.0'} @@ -4400,6 +4423,9 @@ packages: '@smithy/invalid-dependency@3.0.3': resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.7': + resolution: {integrity: sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==} + '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} @@ -4426,6 +4452,10 @@ packages: resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.9': + resolution: {integrity: sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@2.5.1': resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} engines: {node: '>=14.0.0'} @@ -4438,6 +4468,10 @@ packages: resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.1.4': + resolution: {integrity: sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@2.3.1': resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} engines: {node: '>=14.0.0'} @@ -4446,6 +4480,10 @@ packages: resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.23': + resolution: {integrity: sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.7': resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} engines: {node: '>=16.0.0'} @@ -4458,6 +4496,10 @@ packages: resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@3.0.7': + resolution: {integrity: sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@2.2.0': resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} engines: {node: '>=14.0.0'} @@ -4466,6 +4508,10 @@ packages: resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.7': + resolution: {integrity: sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==} + engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@2.3.0': resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} engines: {node: '>=14.0.0'} @@ -4478,6 +4524,10 @@ packages: resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.8': + resolution: {integrity: sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==} + engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@2.5.0': resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} engines: {node: '>=14.0.0'} @@ -4490,6 +4540,10 @@ packages: resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.2.4': + resolution: {integrity: sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==} + engines: {node: '>=16.0.0'} + '@smithy/property-provider@2.2.0': resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} engines: {node: '>=14.0.0'} @@ -4498,6 +4552,10 @@ packages: resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} engines: {node: '>=16.0.0'} + '@smithy/property-provider@3.1.7': + resolution: {integrity: sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==} + engines: {node: '>=16.0.0'} + '@smithy/protocol-http@2.0.5': resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} engines: {node: '>=14.0.0'} @@ -4514,6 +4572,10 @@ packages: resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.1.4': + resolution: {integrity: sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==} + engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@2.2.0': resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} engines: {node: '>=14.0.0'} @@ -4522,6 +4584,10 @@ packages: resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.7': + resolution: {integrity: sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==} + engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@2.2.0': resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} engines: {node: '>=14.0.0'} @@ -4530,6 +4596,10 @@ packages: resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.7': + resolution: {integrity: sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==} + engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@2.1.5': resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} engines: {node: '>=14.0.0'} @@ -4538,18 +4608,22 @@ packages: resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.7': + resolution: {integrity: sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==} + engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@2.4.0': resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} engines: {node: '>=14.0.0'} - '@smithy/shared-ini-file-loader@3.1.3': - resolution: {integrity: sha512-Z8Y3+08vgoDgl4HENqNnnzSISAaGrF2RoKupoC47u2wiMp+Z8P/8mDh1CL8+8ujfi2U5naNvopSBmP/BUj8b5w==} - engines: {node: '>=16.0.0'} - '@smithy/shared-ini-file-loader@3.1.4': resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.8': + resolution: {integrity: sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==} + engines: {node: '>=16.0.0'} + '@smithy/signature-v4@2.3.0': resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} engines: {node: '>=14.0.0'} @@ -4562,6 +4636,10 @@ packages: resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} engines: {node: '>=16.0.0'} + '@smithy/signature-v4@4.2.0': + resolution: {integrity: sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==} + engines: {node: '>=16.0.0'} + '@smithy/smithy-client@2.5.1': resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} engines: {node: '>=14.0.0'} @@ -4574,12 +4652,16 @@ packages: resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.4.0': + resolution: {integrity: sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==} + engines: {node: '>=16.0.0'} + '@smithy/types@2.12.0': resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} engines: {node: '>=14.0.0'} - '@smithy/types@3.3.0': - resolution: {integrity: sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==} + '@smithy/types@3.5.0': + resolution: {integrity: sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==} engines: {node: '>=16.0.0'} '@smithy/url-parser@2.2.0': @@ -4588,6 +4670,9 @@ packages: '@smithy/url-parser@3.0.3': resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + '@smithy/url-parser@3.0.7': + resolution: {integrity: sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==} + '@smithy/util-base64@2.3.0': resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} engines: {node: '>=14.0.0'} @@ -4634,6 +4719,10 @@ packages: resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.23': + resolution: {integrity: sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==} + engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.7': resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} engines: {node: '>= 10.0.0'} @@ -4646,6 +4735,10 @@ packages: resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.23': + resolution: {integrity: sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==} + engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.7': resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} engines: {node: '>= 10.0.0'} @@ -4658,6 +4751,10 @@ packages: resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.1.3': + resolution: {integrity: sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==} + engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@2.2.0': resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} engines: {node: '>=14.0.0'} @@ -4674,6 +4771,10 @@ packages: resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} engines: {node: '>=16.0.0'} + '@smithy/util-middleware@3.0.7': + resolution: {integrity: sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==} + engines: {node: '>=16.0.0'} + '@smithy/util-retry@2.2.0': resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} engines: {node: '>= 14.0.0'} @@ -4682,18 +4783,22 @@ packages: resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.7': + resolution: {integrity: sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==} + engines: {node: '>=16.0.0'} + '@smithy/util-stream@2.2.0': resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} engines: {node: '>=14.0.0'} - '@smithy/util-stream@3.0.5': - resolution: {integrity: sha512-xC3L5PKMAT/Bh8fmHNXP9sdQ4+4aKVUU3EEJ2CF/lLk7R+wtMJM+v/1B4en7jO++Wa5spGzFDBCl0QxgbUc5Ug==} - engines: {node: '>=16.0.0'} - '@smithy/util-stream@3.1.3': resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.1.9': + resolution: {integrity: sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==} + engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@2.2.0': resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} engines: {node: '>=14.0.0'} @@ -5762,7 +5867,6 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@3.0.0-alpha-1: @@ -7830,25 +7934,25 @@ snapshots: '@aws-crypto/crc32@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 2.6.3 '@aws-crypto/crc32c@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 2.6.3 '@aws-crypto/ie11-detection@3.0.0': @@ -7860,7 +7964,7 @@ snapshots: '@aws-crypto/ie11-detection': 3.0.0 '@aws-crypto/supports-web-crypto': 3.0.0 '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -7869,7 +7973,7 @@ snapshots: dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7880,7 +7984,7 @@ snapshots: '@aws-crypto/sha256-js': 3.0.0 '@aws-crypto/supports-web-crypto': 3.0.0 '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 @@ -7890,7 +7994,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7898,19 +8002,19 @@ snapshots: '@aws-crypto/sha256-js@3.0.0': dependencies: '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/sha256-js@4.0.0': dependencies: '@aws-crypto/util': 4.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 2.6.3 '@aws-crypto/supports-web-crypto@3.0.0': @@ -7923,19 +8027,19 @@ snapshots: '@aws-crypto/util@3.0.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 '@aws-crypto/util@4.0.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7956,30 +8060,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8017,7 +8121,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8066,7 +8170,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8115,7 +8219,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8149,30 +8253,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.4 - '@smithy/core': 2.2.4 - '@smithy/fetch-http-handler': 3.2.0 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.3 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.1 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.7 - '@smithy/util-defaults-mode-node': 3.0.7 - '@smithy/util-endpoints': 2.0.4 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8285,7 +8389,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8332,7 +8436,7 @@ snapshots: '@smithy/node-http-handler': 3.1.4 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8380,7 +8484,7 @@ snapshots: '@smithy/node-http-handler': 3.1.1 '@smithy/protocol-http': 4.0.3 '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/url-parser': 3.0.3 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 @@ -8411,30 +8515,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8457,30 +8561,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8502,30 +8606,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.614.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8547,30 +8651,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.637.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8592,30 +8696,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.645.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8673,30 +8777,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8716,30 +8820,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8759,30 +8863,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.614.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8802,30 +8906,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.637.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8845,30 +8949,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.645.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8932,30 +9036,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.598.0 '@aws-sdk/util-user-agent-browser': 3.598.0 '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8977,30 +9081,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9022,30 +9126,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.614.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9067,30 +9171,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.637.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9112,30 +9216,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.645.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9143,46 +9247,46 @@ snapshots: '@aws-sdk/core@3.598.0': dependencies: - '@smithy/core': 2.4.0 - '@smithy/protocol-http': 4.1.0 + '@smithy/core': 2.4.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 '@aws-sdk/core@3.609.0': dependencies: - '@smithy/core': 2.4.0 - '@smithy/protocol-http': 4.1.0 + '@smithy/core': 2.4.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 '@aws-sdk/core@3.620.1': dependencies: - '@smithy/core': 2.4.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/core': 2.4.8 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 4.2.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 fast-xml-parser: 4.2.5 tslib: 2.6.3 '@aws-sdk/core@3.635.0': dependencies: - '@smithy/core': 2.4.0 - '@smithy/node-config-provider': 3.1.4 + '@smithy/core': 2.4.8 + '@smithy/node-config-provider': 3.1.8 '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 fast-xml-parser: 4.4.1 tslib: 2.6.3 @@ -9190,8 +9294,8 @@ snapshots: dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt @@ -9206,70 +9310,70 @@ snapshots: '@aws-sdk/credential-provider-env@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-env@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.635.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.1.3 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-ini@3.387.0': @@ -9296,10 +9400,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9314,10 +9418,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9332,10 +9436,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9350,10 +9454,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9368,10 +9472,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9402,10 +9506,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9421,10 +9525,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9443,7 +9547,7 @@ snapshots: '@smithy/credential-provider-imds': 3.2.0 '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9459,10 +9563,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9478,10 +9582,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9499,25 +9603,25 @@ snapshots: '@aws-sdk/credential-provider-process@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-process@3.620.1': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-sso@3.387.0': @@ -9537,9 +9641,9 @@ snapshots: '@aws-sdk/client-sso': 3.598.0 '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9550,9 +9654,9 @@ snapshots: '@aws-sdk/client-sso': 3.609.0 '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9563,9 +9667,9 @@ snapshots: '@aws-sdk/client-sso': 3.620.1 '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9576,9 +9680,9 @@ snapshots: '@aws-sdk/client-sso': 3.637.0 '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9589,9 +9693,9 @@ snapshots: '@aws-sdk/client-sso': 3.645.0 '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9608,40 +9712,40 @@ snapshots: dependencies: '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.620.1)': dependencies: '@aws-sdk/client-sts': 3.620.1 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0)': dependencies: '@aws-sdk/client-sts': 3.637.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0)': dependencies: '@aws-sdk/client-sts': 3.645.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': @@ -9658,9 +9762,9 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.1.3 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -9684,9 +9788,9 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 @@ -9694,18 +9798,18 @@ snapshots: dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-endpoint-discovery@3.620.0': dependencies: '@aws-sdk/endpoint-cache': 3.572.0 '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-expect-continue@3.387.0': @@ -9718,8 +9822,8 @@ snapshots: '@aws-sdk/middleware-expect-continue@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.0.3 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-flexible-checksums@3.387.0': @@ -9739,8 +9843,8 @@ snapshots: '@aws-crypto/crc32c': 5.2.0 '@aws-sdk/types': 3.598.0 '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -9754,22 +9858,22 @@ snapshots: '@aws-sdk/middleware-host-header@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-host-header@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-location-constraint@3.387.0': @@ -9781,7 +9885,7 @@ snapshots: '@aws-sdk/middleware-location-constraint@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-logger@3.387.0': @@ -9793,13 +9897,13 @@ snapshots: '@aws-sdk/middleware-logger@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.387.0': @@ -9812,22 +9916,22 @@ snapshots: '@aws-sdk/middleware-recursion-detection@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-sdk-s3@3.387.0': @@ -9842,11 +9946,11 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 @@ -9854,21 +9958,21 @@ snapshots: dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 4.2.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-stream': 3.1.3 + '@smithy/util-stream': 3.1.9 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 '@aws-sdk/middleware-sdk-sqs@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -9894,10 +9998,10 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/middleware-ssec@3.387.0': @@ -9909,7 +10013,7 @@ snapshots: '@aws-sdk/middleware-ssec@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.387.0': @@ -9924,67 +10028,67 @@ snapshots: dependencies: '@aws-sdk/types': 3.598.0 '@aws-sdk/util-endpoints': 3.598.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.620.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.614.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.637.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.645.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/s3-request-presigner@3.623.0': @@ -9995,7 +10099,7 @@ snapshots: '@smithy/middleware-endpoint': 3.1.0 '@smithy/protocol-http': 4.1.0 '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/signature-v4-multi-region@3.387.0': @@ -10010,18 +10114,18 @@ snapshots: dependencies: '@aws-sdk/middleware-sdk-s3': 3.598.0 '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/signature-v4-multi-region@3.622.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.622.0 '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/signature-v4': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 4.2.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.387.0': @@ -10036,45 +10140,45 @@ snapshots: dependencies: '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': dependencies: '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/types@3.387.0': @@ -10084,12 +10188,17 @@ snapshots: '@aws-sdk/types@3.598.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/types@3.609.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@aws-sdk/types@3.667.0': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-arn-parser@3.310.0': @@ -10128,43 +10237,43 @@ snapshots: '@aws-sdk/util-endpoints@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.637.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.645.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 - '@smithy/util-endpoints': 2.0.5 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-format-url@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-locate-window@3.568.0': @@ -10181,14 +10290,14 @@ snapshots: '@aws-sdk/util-user-agent-browser@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 @@ -10202,22 +10311,22 @@ snapshots: '@aws-sdk/util-user-agent-node@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-node@3.614.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-utf8-browser@3.259.0': @@ -10230,7 +10339,7 @@ snapshots: '@aws-sdk/xml-builder@3.598.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@babel/code-frame@7.24.7': @@ -10640,7 +10749,7 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.6-b': + '@pagopa/interop-outbound-models@1.0.10': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 @@ -10794,7 +10903,12 @@ snapshots: '@smithy/abort-controller@3.1.1': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/abort-controller@3.1.5': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/chunked-blob-reader-native@2.2.0': @@ -10825,41 +10939,62 @@ snapshots: '@smithy/config-resolver@3.0.4': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/config-resolver@3.0.5': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 + tslib: 2.6.3 + + '@smithy/config-resolver@3.0.9': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/core@2.2.4': dependencies: - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 - '@smithy/middleware-serde': 3.0.3 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/core@2.4.0': dependencies: - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + + '@smithy/core@2.4.8': + dependencies: + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-middleware': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -10871,20 +11006,20 @@ snapshots: '@smithy/url-parser': 2.2.0 tslib: 2.6.3 - '@smithy/credential-provider-imds@3.1.3': + '@smithy/credential-provider-imds@3.2.0': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 tslib: 2.6.3 - '@smithy/credential-provider-imds@3.2.0': + '@smithy/credential-provider-imds@3.2.4': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 tslib: 2.6.3 '@smithy/eventstream-codec@2.2.0': @@ -10897,7 +11032,7 @@ snapshots: '@smithy/eventstream-codec@3.1.2': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 tslib: 2.6.3 @@ -10910,7 +11045,7 @@ snapshots: '@smithy/eventstream-serde-browser@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/eventstream-serde-config-resolver@2.2.0': @@ -10920,7 +11055,7 @@ snapshots: '@smithy/eventstream-serde-config-resolver@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/eventstream-serde-node@2.2.0': @@ -10932,7 +11067,7 @@ snapshots: '@smithy/eventstream-serde-node@3.0.4': dependencies: '@smithy/eventstream-serde-universal': 3.0.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/eventstream-serde-universal@2.2.0': @@ -10944,7 +11079,7 @@ snapshots: '@smithy/eventstream-serde-universal@3.0.4': dependencies: '@smithy/eventstream-codec': 3.1.2 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/fetch-http-handler@2.5.0': @@ -10957,17 +11092,25 @@ snapshots: '@smithy/fetch-http-handler@3.2.0': dependencies: - '@smithy/protocol-http': 4.0.3 - '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 tslib: 2.6.3 '@smithy/fetch-http-handler@3.2.4': dependencies: - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-base64': 3.0.0 + tslib: 2.6.3 + + '@smithy/fetch-http-handler@3.2.9': + dependencies: + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 tslib: 2.6.3 @@ -10982,7 +11125,7 @@ snapshots: dependencies: '@smithy/chunked-blob-reader': 3.0.0 '@smithy/chunked-blob-reader-native': 3.0.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/hash-node@2.2.0': @@ -10994,7 +11137,14 @@ snapshots: '@smithy/hash-node@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + + '@smithy/hash-node@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11007,7 +11157,7 @@ snapshots: '@smithy/hash-stream-node@3.1.2': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11018,7 +11168,12 @@ snapshots: '@smithy/invalid-dependency@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/invalid-dependency@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/is-array-buffer@2.2.0': @@ -11037,7 +11192,7 @@ snapshots: '@smithy/md5-js@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11049,14 +11204,20 @@ snapshots: '@smithy/middleware-content-length@3.0.3': dependencies: - '@smithy/protocol-http': 4.0.3 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/middleware-content-length@3.0.5': dependencies: - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-content-length@3.0.9': + dependencies: + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/middleware-endpoint@2.5.1': @@ -11071,22 +11232,32 @@ snapshots: '@smithy/middleware-endpoint@3.0.4': dependencies: - '@smithy/middleware-serde': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/shared-ini-file-loader': 3.1.3 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-middleware': 3.0.3 + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/middleware-endpoint@3.1.0': dependencies: - '@smithy/middleware-serde': 3.0.3 - '@smithy/node-config-provider': 3.1.4 + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-middleware': 3.0.3 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 + tslib: 2.6.3 + + '@smithy/middleware-endpoint@3.1.4': + dependencies: + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/middleware-retry@2.3.1': @@ -11103,25 +11274,37 @@ snapshots: '@smithy/middleware-retry@3.0.15': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 '@smithy/service-error-classification': 3.0.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 + tslib: 2.6.3 + uuid: 9.0.1 + + '@smithy/middleware-retry@3.0.23': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/service-error-classification': 3.0.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 tslib: 2.6.3 uuid: 9.0.1 '@smithy/middleware-retry@3.0.7': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/service-error-classification': 3.0.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/service-error-classification': 3.0.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 tslib: 2.6.3 uuid: 9.0.1 @@ -11132,7 +11315,12 @@ snapshots: '@smithy/middleware-serde@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-serde@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/middleware-stack@2.2.0': @@ -11142,7 +11330,12 @@ snapshots: '@smithy/middleware-stack@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-stack@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-config-provider@2.3.0': @@ -11154,16 +11347,23 @@ snapshots: '@smithy/node-config-provider@3.1.3': dependencies: - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-config-provider@3.1.4': dependencies: '@smithy/property-provider': 3.1.3 '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/node-config-provider@3.1.8': + dependencies: + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-http-handler@2.5.0': @@ -11176,18 +11376,26 @@ snapshots: '@smithy/node-http-handler@3.1.1': dependencies: - '@smithy/abort-controller': 3.1.1 - '@smithy/protocol-http': 4.1.0 - '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/abort-controller': 3.1.5 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/node-http-handler@3.1.4': dependencies: '@smithy/abort-controller': 3.1.1 - '@smithy/protocol-http': 4.1.0 + '@smithy/protocol-http': 4.1.4 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/node-http-handler@3.2.4': + dependencies: + '@smithy/abort-controller': 3.1.5 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/property-provider@2.2.0': @@ -11197,7 +11405,12 @@ snapshots: '@smithy/property-provider@3.1.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/property-provider@3.1.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/protocol-http@2.0.5': @@ -11212,12 +11425,17 @@ snapshots: '@smithy/protocol-http@4.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/protocol-http@4.1.0': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/protocol-http@4.1.4': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/querystring-builder@2.2.0': @@ -11228,7 +11446,13 @@ snapshots: '@smithy/querystring-builder@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-uri-escape': 3.0.0 + tslib: 2.6.3 + + '@smithy/querystring-builder@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 @@ -11239,7 +11463,12 @@ snapshots: '@smithy/querystring-parser@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/querystring-parser@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/service-error-classification@2.1.5': @@ -11248,21 +11477,25 @@ snapshots: '@smithy/service-error-classification@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + + '@smithy/service-error-classification@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/shared-ini-file-loader@2.4.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - '@smithy/shared-ini-file-loader@3.1.3': + '@smithy/shared-ini-file-loader@3.1.4': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/shared-ini-file-loader@3.1.4': + '@smithy/shared-ini-file-loader@3.1.8': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/signature-v4@2.3.0': @@ -11278,9 +11511,9 @@ snapshots: '@smithy/signature-v4@3.1.2': dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11288,10 +11521,21 @@ snapshots: '@smithy/signature-v4@4.1.0': dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-uri-escape': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + + '@smithy/signature-v4@4.2.0': + dependencies: + '@smithy/is-array-buffer': 3.0.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-middleware': 3.0.7 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -11307,27 +11551,36 @@ snapshots: '@smithy/smithy-client@3.1.5': dependencies: - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-stack': 3.0.3 - '@smithy/protocol-http': 4.0.3 - '@smithy/types': 3.3.0 - '@smithy/util-stream': 3.0.5 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@smithy/smithy-client@3.2.0': dependencies: - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-stack': 3.0.3 - '@smithy/protocol-http': 4.1.0 - '@smithy/types': 3.3.0 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 '@smithy/util-stream': 3.1.3 tslib: 2.6.3 + '@smithy/smithy-client@3.4.0': + dependencies: + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 + tslib: 2.6.3 + '@smithy/types@2.12.0': dependencies: tslib: 2.6.3 - '@smithy/types@3.3.0': + '@smithy/types@3.5.0': dependencies: tslib: 2.6.3 @@ -11340,7 +11593,13 @@ snapshots: '@smithy/url-parser@3.0.3': dependencies: '@smithy/querystring-parser': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/url-parser@3.0.7': + dependencies: + '@smithy/querystring-parser': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-base64@2.3.0': @@ -11400,16 +11659,24 @@ snapshots: '@smithy/util-defaults-mode-browser@3.0.15': dependencies: '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + bowser: 2.11.0 + tslib: 2.6.3 + + '@smithy/util-defaults-mode-browser@3.0.23': + dependencies: + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 '@smithy/util-defaults-mode-browser@3.0.7': dependencies: - '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 @@ -11425,34 +11692,50 @@ snapshots: '@smithy/util-defaults-mode-node@3.0.15': dependencies: - '@smithy/config-resolver': 3.0.5 + '@smithy/config-resolver': 3.0.9 '@smithy/credential-provider-imds': 3.2.0 - '@smithy/node-config-provider': 3.1.4 + '@smithy/node-config-provider': 3.1.8 '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-defaults-mode-node@3.0.23': + dependencies: + '@smithy/config-resolver': 3.0.9 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-defaults-mode-node@3.0.7': dependencies: - '@smithy/config-resolver': 3.0.5 - '@smithy/credential-provider-imds': 3.1.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.3.0 + '@smithy/config-resolver': 3.0.9 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-endpoints@2.0.4': dependencies: - '@smithy/node-config-provider': 3.1.3 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-endpoints@2.0.5': dependencies: - '@smithy/node-config-provider': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-endpoints@2.1.3': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-hex-encoding@2.2.0': @@ -11470,7 +11753,12 @@ snapshots: '@smithy/util-middleware@3.0.3': dependencies: - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-middleware@3.0.7': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-retry@2.2.0': @@ -11482,7 +11770,13 @@ snapshots: '@smithy/util-retry@3.0.3': dependencies: '@smithy/service-error-classification': 3.0.3 - '@smithy/types': 3.3.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-retry@3.0.7': + dependencies: + '@smithy/service-error-classification': 3.0.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/util-stream@2.2.0': @@ -11496,22 +11790,22 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - '@smithy/util-stream@3.0.5': + '@smithy/util-stream@3.1.3': dependencies: - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/util-stream@3.1.3': + '@smithy/util-stream@3.1.9': dependencies: - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/types': 3.3.0 + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-hex-encoding': 3.0.0 @@ -11544,8 +11838,8 @@ snapshots: '@smithy/util-waiter@3.1.2': dependencies: - '@smithy/abort-controller': 3.1.1 - '@smithy/types': 3.3.0 + '@smithy/abort-controller': 3.1.5 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@testcontainers/postgresql@10.9.0': @@ -12223,7 +12517,7 @@ snapshots: get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 - type-detect: 4.0.8 + type-detect: 4.1.0 chalk@2.4.2: dependencies: From 495e5c6ae2d2606b347839c0a389032475f256a6 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 13 Nov 2024 16:23:07 +0100 Subject: [PATCH 028/126] PIN-5659 - Added `DELETE /tenants/delegatedProducer` in bff and tenant process (#1189) --- .../tenant/Add delegated producer feature.bru | 16 +++ .../Remove delegated producer feature.bru | 16 +++ packages/api-clients/open-api/bffApi.yml | 59 ++++++++++ packages/api-clients/open-api/tenantApi.yml | 22 ++++ .../src/routers/tenantRouter.ts | 18 +++ .../src/services/tenantService.ts | 10 ++ .../compute-agreements-consumer/src/index.ts | 3 +- packages/models/proto/v2/tenant/events.proto | 4 + packages/models/src/tenant/tenantEvents.ts | 9 ++ .../src/converters/toOutboundEventV2.ts | 1 + .../tenant-process/src/model/domain/errors.ts | 11 ++ .../src/model/domain/toEvent.ts | 17 +++ .../src/routers/TenantRouter.ts | 25 ++++ .../src/services/tenantService.ts | 42 +++++++ .../tenant-process/src/services/validators.ts | 7 ++ .../src/utilities/errorMappers.ts | 10 ++ ...moveTenantDelegatedProducerFeature.test.ts | 111 ++++++++++++++++++ .../src/tenantConsumerServiceV2.ts | 1 + 18 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 collections/tenant/Add delegated producer feature.bru create mode 100644 collections/tenant/Remove delegated producer feature.bru create mode 100644 packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts diff --git a/collections/tenant/Add delegated producer feature.bru b/collections/tenant/Add delegated producer feature.bru new file mode 100644 index 0000000000..d7790c4887 --- /dev/null +++ b/collections/tenant/Add delegated producer feature.bru @@ -0,0 +1,16 @@ +meta { + name: Add delegated producer feature from tenant caller + type: http + seq: 12 +} + +post { + url: {{host-tenant}}/tenants/delegatedProducer + body: json + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/collections/tenant/Remove delegated producer feature.bru b/collections/tenant/Remove delegated producer feature.bru new file mode 100644 index 0000000000..bada9133ea --- /dev/null +++ b/collections/tenant/Remove delegated producer feature.bru @@ -0,0 +1,16 @@ +meta { + name: Remove delegated producer feature from tenant caller + type: http + seq: 12 +} + +delete { + url: {{host-tenant}}/tenants/delegatedProducer + body: json + auth: none +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 2c9ab3e0e7..9542323821 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -8382,6 +8382,65 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + delete: + tags: + - tenants + summary: Delete delegated producer feature to tenant caller + operationId: deleteTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature deleted + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "409": + description: Feature not assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available /clients: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" diff --git a/packages/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index 5cf569ea6d..b82f6cf08c 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -996,6 +996,8 @@ paths: schema: $ref: "#/components/schemas/Problem" /tenants/delegatedProducer: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" post: tags: - tenant @@ -1016,6 +1018,26 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + delete: + tags: + - tenant + summary: Remove delegated producer feature to tenant caller + operationId: removeTenantDelegatedProducerFeature + responses: + "204": + description: Delegated producer feature removed + "409": + description: Feature not assigned to tenant + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /status: get: security: [] diff --git a/packages/backend-for-frontend/src/routers/tenantRouter.ts b/packages/backend-for-frontend/src/routers/tenantRouter.ts index ef647d3b7f..5f93f610e5 100644 --- a/packages/backend-for-frontend/src/routers/tenantRouter.ts +++ b/packages/backend-for-frontend/src/routers/tenantRouter.ts @@ -423,6 +423,24 @@ const tenantRouter = ( ); return res.status(errorRes.status).send(errorRes); } + }) + .delete("/tenants/delegatedProducer", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + const tenantId = ctx.authData.organizationId; + + try { + await tenantService.removeTenantDelegatedProducerFeature(tenantId, ctx); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error while removing delegated producer feature to ${tenantId}` + ); + return res.status(errorRes.status).send(errorRes); + } }); return tenantRouter; diff --git a/packages/backend-for-frontend/src/services/tenantService.ts b/packages/backend-for-frontend/src/services/tenantService.ts index a545f32ef9..82dac3d5f6 100644 --- a/packages/backend-for-frontend/src/services/tenantService.ts +++ b/packages/backend-for-frontend/src/services/tenantService.ts @@ -428,6 +428,16 @@ export function tenantServiceBuilder( { headers } ); }, + async removeTenantDelegatedProducerFeature( + tenantId: TenantId, + { logger, headers }: WithLogger + ): Promise { + logger.info(`Removing delegated producer feature to tenant ${tenantId}`); + await tenantProcessClient.tenant.removeTenantDelegatedProducerFeature( + undefined, + { headers } + ); + }, }; } diff --git a/packages/compute-agreements-consumer/src/index.ts b/packages/compute-agreements-consumer/src/index.ts index 116ddc08b9..886b17f248 100644 --- a/packages/compute-agreements-consumer/src/index.ts +++ b/packages/compute-agreements-consumer/src/index.ts @@ -96,7 +96,8 @@ async function processMessage({ "TenantMailDeleted", "TenantMailAdded", "MaintenanceTenantPromotedToCertifier", - "TenantDelegatedProducerFeatureAdded" + "TenantDelegatedProducerFeatureAdded", + "TenantDelegatedProducerFeatureRemoved" ), }, () => Promise.resolve() diff --git a/packages/models/proto/v2/tenant/events.proto b/packages/models/proto/v2/tenant/events.proto index 70fb333ef8..55a2b71317 100644 --- a/packages/models/proto/v2/tenant/events.proto +++ b/packages/models/proto/v2/tenant/events.proto @@ -83,3 +83,7 @@ message MaintenanceTenantPromotedToCertifierV2{ message TenantDelegatedProducerFeatureAddedV2 { TenantV2 tenant = 1; } + +message TenantDelegatedProducerFeatureRemovedV2 { + TenantV2 tenant = 1; +} diff --git a/packages/models/src/tenant/tenantEvents.ts b/packages/models/src/tenant/tenantEvents.ts index 14321eb4d4..cf30f12a6d 100644 --- a/packages/models/src/tenant/tenantEvents.ts +++ b/packages/models/src/tenant/tenantEvents.ts @@ -26,6 +26,7 @@ import { TenantMailDeletedV2, TenantKindUpdatedV2, TenantDelegatedProducerFeatureAddedV2, + TenantDelegatedProducerFeatureRemovedV2, MaintenanceTenantUpdatedV2, } from "../gen/v2/tenant/events.js"; import { protobufDecoder } from "../protobuf/protobuf.js"; @@ -117,6 +118,9 @@ export function tenantEventToBinaryDataV2(event: TenantEventV2): Uint8Array { .with({ type: "TenantDelegatedProducerFeatureAdded" }, ({ data }) => TenantDelegatedProducerFeatureAddedV2.toBinary(data) ) + .with({ type: "TenantDelegatedProducerFeatureRemoved" }, ({ data }) => + TenantDelegatedProducerFeatureRemovedV2.toBinary(data) + ) .exhaustive(); } @@ -246,6 +250,11 @@ export const TenantEventV2 = z.discriminatedUnion("type", [ type: z.literal("TenantDelegatedProducerFeatureAdded"), data: protobufDecoder(TenantDelegatedProducerFeatureAddedV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("TenantDelegatedProducerFeatureRemoved"), + data: protobufDecoder(TenantDelegatedProducerFeatureRemovedV2), + }), ]); export type TenantEventV2 = z.infer; diff --git a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts index 4ba155f4d4..cb9eeb2b6d 100644 --- a/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/tenant-outbound-writer/src/converters/toOutboundEventV2.ts @@ -47,6 +47,7 @@ export function toOutboundEventV2( { type: "MaintenanceTenantPromotedToCertifier" }, { type: "MaintenanceTenantUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, + { type: "TenantDelegatedProducerFeatureRemoved" }, (msg) => ({ event_version: msg.event_version, type: msg.type, diff --git a/packages/tenant-process/src/model/domain/errors.ts b/packages/tenant-process/src/model/domain/errors.ts index f150139a78..1f07279c8c 100644 --- a/packages/tenant-process/src/model/domain/errors.ts +++ b/packages/tenant-process/src/model/domain/errors.ts @@ -34,6 +34,7 @@ export const errorCodes = { attributeNotFoundInTenant: "0025", tenantNotFoundByExternalId: "0026", tenantAlreadyHasDelegatedProducerFeature: "0027", + tenantHasNoDelegatedProducerFeature: "0028", }; export type ErrorCodes = keyof typeof errorCodes; @@ -303,3 +304,13 @@ export function tenantAlreadyHasDelegatedProducerFeature( title: "Feature already assigned", }); } + +export function tenantHasNoDelegatedProducerFeature( + tenantId: TenantId +): ApiError { + return new ApiError({ + detail: `Tenant ${tenantId} has no delegated producer feature assigned`, + code: "tenantHasNoDelegatedProducerFeature", + title: "Feature not assigned", + }); +} diff --git a/packages/tenant-process/src/model/domain/toEvent.ts b/packages/tenant-process/src/model/domain/toEvent.ts index 5b65157807..6d889c6265 100644 --- a/packages/tenant-process/src/model/domain/toEvent.ts +++ b/packages/tenant-process/src/model/domain/toEvent.ts @@ -319,3 +319,20 @@ export const toCreateEventTenantDelegatedProducerFeatureAdded = ( }, correlationId, }); + +export const toCreateEventTenantDelegatedProducerFeatureRemoved = ( + version: number, + updatedTenant: Tenant, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: updatedTenant.id, + version, + event: { + type: "TenantDelegatedProducerFeatureRemoved", + event_version: 2, + data: { + tenant: toTenantV2(updatedTenant), + }, + }, + correlationId, +}); diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index 4300013729..2416c5457d 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -39,6 +39,7 @@ import { m2mUpsertTenantErrorMapper, maintenanceTenantUpdatedErrorMapper, assignTenantDelegatedProducerFeatureErrorMapper, + removeTenantDelegatedProducerFeatureErrorMapper, } from "../utilities/errorMappers.js"; import { readModelServiceBuilder } from "../services/readModelService.js"; import { config } from "../config/config.js"; @@ -521,6 +522,30 @@ const tenantsRouter = ( return res.status(errorRes.status).send(errorRes); } } + ) + .delete( + "/tenants/delegatedProducer", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + try { + await tenantService.removeTenantDelegatedProducerFeature({ + organizationId: req.ctx.authData.organizationId, + correlationId: req.ctx.correlationId, + authData: ctx.authData, + logger: ctx.logger, + }); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + removeTenantDelegatedProducerFeatureErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } ); const m2mRouter = ctx.router(tenantApi.m2mApi.api, { diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index 1c4cd1444c..0b1181838c 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -53,6 +53,7 @@ import { toCreateEventTenantVerifiedAttributeRevoked, toCreateEventMaintenanceTenantUpdated, toCreateEventTenantDelegatedProducerFeatureAdded, + toCreateEventTenantDelegatedProducerFeatureRemoved, } from "../model/domain/toEvent.js"; import { attributeAlreadyRevoked, @@ -87,6 +88,7 @@ import { assertRequesterIPAOrigin, assertDelegatedProducerFeatureNotAssigned, getTenantKind, + assertDelegatedProducerFeatureAssigned, } from "./validators.js"; import { ReadModelService } from "./readModelService.js"; @@ -1669,6 +1671,46 @@ export function tenantServiceBuilder( ) ); }, + async removeTenantDelegatedProducerFeature({ + organizationId, + correlationId, + authData, + logger, + }: { + organizationId: TenantId; + correlationId: CorrelationId; + authData: AuthData; + logger: Logger; + }): Promise { + logger.info( + `Removing delegated producer feature to tenant ${organizationId}` + ); + + assertRequesterIPAOrigin(authData); + + const requesterTenant = await retrieveTenant( + organizationId, + readModelService + ); + + assertDelegatedProducerFeatureAssigned(requesterTenant.data); + + const updatedTenant: Tenant = { + ...requesterTenant.data, + features: requesterTenant.data.features.filter( + (f) => f.type !== "DelegatedProducer" + ), + updatedAt: new Date(), + }; + + await repository.createEvent( + toCreateEventTenantDelegatedProducerFeatureRemoved( + requesterTenant.metadata.version, + updatedTenant, + correlationId + ) + ); + }, }; } diff --git a/packages/tenant-process/src/services/validators.ts b/packages/tenant-process/src/services/validators.ts index d99263f4da..706d86e79f 100644 --- a/packages/tenant-process/src/services/validators.ts +++ b/packages/tenant-process/src/services/validators.ts @@ -32,6 +32,7 @@ import { verifiedAttributeSelfVerificationNotAllowed, attributeNotFound, tenantAlreadyHasDelegatedProducerFeature, + tenantHasNoDelegatedProducerFeature, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -263,3 +264,9 @@ export function assertDelegatedProducerFeatureNotAssigned( throw tenantAlreadyHasDelegatedProducerFeature(tenant.id); } } + +export function assertDelegatedProducerFeatureAssigned(tenant: Tenant): void { + if (!tenant.features.some((f) => f.type === "DelegatedProducer")) { + throw tenantHasNoDelegatedProducerFeature(tenant.id); + } +} diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index fc44f5b714..f34e04ec9e 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -241,8 +241,18 @@ export const assignTenantDelegatedProducerFeatureErrorMapper = ( error: ApiError ): number => match(error.code) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) .with( "tenantAlreadyHasDelegatedProducerFeature", () => HTTP_STATUS_CONFLICT ) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const removeTenantDelegatedProducerFeatureErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) + .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("tenantHasNoDelegatedProducerFeature", () => HTTP_STATUS_CONFLICT) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts b/packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts new file mode 100644 index 0000000000..6f90ebaded --- /dev/null +++ b/packages/tenant-process/test/removeTenantDelegatedProducerFeature.test.ts @@ -0,0 +1,111 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + generateId, + Tenant, + protobufDecoder, + toTenantV2, + TenantDelegatedProducerFeatureRemovedV2, + operationForbidden, +} from "pagopa-interop-models"; +import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; +import { readLastEventByStreamId } from "pagopa-interop-commons-test/dist/eventStoreTestUtils.js"; +import { getMockAuthData, getMockTenant } from "pagopa-interop-commons-test"; +import { tenantHasNoDelegatedProducerFeature } from "../src/model/domain/errors.js"; +import { addOneTenant, postgresDB, tenantService } from "./utils.js"; + +describe("removeTenantDelegatedProducerFeature", async () => { + beforeAll(async () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + it("Should correctly remove the feature", async () => { + const mockTenant: Tenant = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(mockTenant); + await tenantService.removeTenantDelegatedProducerFeature({ + organizationId: mockTenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }); + const writtenEvent = await readLastEventByStreamId( + mockTenant.id, + "tenant", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockTenant.id, + version: "1", + type: "TenantDelegatedProducerFeatureRemoved", + event_version: 2, + }); + + const writtenPayload: TenantDelegatedProducerFeatureRemovedV2 | undefined = + protobufDecoder(TenantDelegatedProducerFeatureRemovedV2).parse( + writtenEvent.data + ); + + const updatedTenant: Tenant = { + ...mockTenant, + features: [], + updatedAt: new Date(), + }; + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + }); + + it("Should throw tenantHasNoDelegatedProducerFeature if the requester tenant already has the delegated producer feature", async () => { + const tenant: Tenant = { + ...getMockTenant(), + features: [], + }; + + await addOneTenant(tenant); + + expect( + tenantService.removeTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: getMockAuthData(), + logger: genericLogger, + }) + ).rejects.toThrowError(tenantHasNoDelegatedProducerFeature(tenant.id)); + }); + it("Should throw operationForbidden if the requester tenant is not a public administration", async () => { + const tenant: Tenant = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer", + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(tenant); + + expect( + tenantService.removeTenantDelegatedProducerFeature({ + organizationId: tenant.id, + correlationId: generateId(), + authData: { + ...getMockAuthData(), + externalId: { origin: "UNKNOWN", value: "test" }, + }, + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); +}); diff --git a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts index e5637e7943..85acd740db 100644 --- a/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts +++ b/packages/tenant-readmodel-writer/src/tenantConsumerServiceV2.ts @@ -36,6 +36,7 @@ export async function handleMessageV2( { type: "TenantMailDeleted" }, { type: "TenantKindUpdated" }, { type: "TenantDelegatedProducerFeatureAdded" }, + { type: "TenantDelegatedProducerFeatureRemoved" }, async (message) => await tenants.updateOne( { From 303e6a2af806202075eb460ef3ea482ac671740f Mon Sep 17 00:00:00 2001 From: Michele De Simone Date: Mon, 18 Nov 2024 15:05:23 +0100 Subject: [PATCH 029/126] feat(workflows): adapt build&push for GitFlow --- .github/workflows/build-push.yaml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-push.yaml b/.github/workflows/build-push.yaml index 4909509cc3..be359746bd 100644 --- a/.github/workflows/build-push.yaml +++ b/.github/workflows/build-push.yaml @@ -3,7 +3,8 @@ name: "Build & Push" on: push: branches: - - "main" + - "develop" + - "hotfix/*" tags: - "*" paths: @@ -55,8 +56,16 @@ jobs: id: login-ecr uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2 - - name: (latest) Docker metadata - id: meta_latest + - name: Normalize ref name + id: norm_ref + run: | + set -euo pipefail + + NORM_REF="$(echo ${{ github.ref_name }} | sed -e 's/\//-/g')" + echo "NORM_REF=$NORM_REF" >> $GITHUB_ENV + + - name: (branch) Docker metadata + id: meta_branch if: ${{ github.ref_type == 'branch' }} uses: docker/metadata-action@60a0d343a0d8a18aedee9d34e62251f752153bdb with: @@ -66,7 +75,7 @@ jobs: prefix= suffix= tags: | - type=raw,value=2.x-latest + type=raw,value=${{ env.NORM_REF }} - name: (tag) Docker metadata id: meta_tag @@ -88,5 +97,5 @@ jobs: context: . file: packages/${{ matrix.package }}/Dockerfile push: true - tags: ${{ github.ref_type == 'branch' && steps.meta_latest.outputs.tags || steps.meta_tag.outputs.tags }} - labels: ${{ github.ref_type == 'branch' && steps.meta_latest.outputs.labels || steps.meta_tag.outputs.labels }} + tags: ${{ github.ref_type == 'branch' && steps.meta_branch.outputs.tags || steps.meta_tag.outputs.tags }} + labels: ${{ github.ref_type == 'branch' && steps.meta_branch.outputs.labels || steps.meta_tag.outputs.labels }} From 1d1987f11c82a1f9813c2b0ef44849a3fc5f8256 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 18 Nov 2024 18:23:17 +0100 Subject: [PATCH 030/126] IMN-521 Authorization-server (#1134) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- docker/docker-compose.yml | 2 +- package.json | 1 + .../open-api/authorizationServerApi.yml | 215 +++++ packages/api-clients/src/index.ts | 1 + .../test/sample.integration.test.ts | 46 - packages/authorization-server/.env | 40 + packages/authorization-server/Dockerfile | 51 ++ .../authorization-server/aws.config.local | 12 + packages/authorization-server/package.json | 53 ++ packages/authorization-server/src/app.ts | 24 + .../authorization-server/src/config/config.ts | 43 + packages/authorization-server/src/index.ts | 7 + .../src/model/domain/errors.ts | 124 +++ .../src/model/domain/models.ts | 5 + .../src/routers/AuthorizationServerRouter.ts | 104 +++ .../src/routers/HealthRouter.ts | 8 + .../src/services/tokenService.ts | 412 +++++++++ .../src/utilities/errorMappers.ts | 23 + .../authorization-server/test/.eslintrc.json | 7 + .../authorizationServer.integration.test.ts | 808 ++++++++++++++++++ .../test/authorizationServer.unit.test.ts | 272 ++++++ .../authorization-server/test/tsconfig.json | 4 + packages/authorization-server/test/utils.ts | 127 +++ .../test/vitestGlobalSetup.ts | 3 + .../authorization-server/tsconfig.check.json | 7 + packages/authorization-server/tsconfig.json | 7 + .../authorization-server/vitest.config.ts | 11 + packages/backend-for-frontend/.env | 2 +- .../src/services/toolService.ts | 86 +- .../client-assertion-validation/src/errors.ts | 88 +- .../client-assertion-validation/src/types.ts | 76 +- .../client-assertion-validation/src/utils.ts | 8 +- .../src/validation.ts | 50 +- .../client-assertion-validation/test/utils.ts | 131 +-- .../test/validation.test.ts | 197 +++-- packages/commons-test/package.json | 1 + packages/commons-test/src/testUtils.ts | 118 ++- .../src/tokenGenerationReadmodelUtils.ts | 36 + ...uthorizationServerTokenGenerationConfig.ts | 22 + packages/commons/src/config/index.ts | 1 + .../src/interop-token/interopTokenService.ts | 175 +++- packages/commons/src/interop-token/models.ts | 28 + packages/commons/src/utils/date.ts | 12 + packages/models/src/brandedIds.ts | 4 +- .../clientAssertionValidation.ts | 41 + packages/models/src/index.ts | 2 + .../src/token-generation-audit/audit.ts | 48 ++ .../token-generation-states-entry.ts | 6 + pnpm-lock.yaml | 137 ++- 49 files changed, 3229 insertions(+), 457 deletions(-) create mode 100644 packages/api-clients/open-api/authorizationServerApi.yml delete mode 100644 packages/authorization-platformstate-writer/test/sample.integration.test.ts create mode 100644 packages/authorization-server/.env create mode 100644 packages/authorization-server/Dockerfile create mode 100644 packages/authorization-server/aws.config.local create mode 100644 packages/authorization-server/package.json create mode 100644 packages/authorization-server/src/app.ts create mode 100644 packages/authorization-server/src/config/config.ts create mode 100644 packages/authorization-server/src/index.ts create mode 100644 packages/authorization-server/src/model/domain/errors.ts create mode 100644 packages/authorization-server/src/model/domain/models.ts create mode 100644 packages/authorization-server/src/routers/AuthorizationServerRouter.ts create mode 100644 packages/authorization-server/src/routers/HealthRouter.ts create mode 100644 packages/authorization-server/src/services/tokenService.ts create mode 100644 packages/authorization-server/src/utilities/errorMappers.ts create mode 100644 packages/authorization-server/test/.eslintrc.json create mode 100644 packages/authorization-server/test/authorizationServer.integration.test.ts create mode 100644 packages/authorization-server/test/authorizationServer.unit.test.ts create mode 100644 packages/authorization-server/test/tsconfig.json create mode 100644 packages/authorization-server/test/utils.ts create mode 100644 packages/authorization-server/test/vitestGlobalSetup.ts create mode 100644 packages/authorization-server/tsconfig.check.json create mode 100644 packages/authorization-server/tsconfig.json create mode 100644 packages/authorization-server/vitest.config.ts create mode 100644 packages/commons/src/config/authorizationServerTokenGenerationConfig.ts create mode 100644 packages/models/src/client-assertion/clientAssertionValidation.ts create mode 100644 packages/models/src/token-generation-audit/audit.ts diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 1d08bc9c6e..c4f5c4a841 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -78,7 +78,7 @@ services: - dynamodb-local restart: always ports: - - "8002:8002" + - "8002:8001" environment: - DYNAMO_ENDPOINT=http://dynamodb-local:8000 - AWS_REGION=eu-south-1 diff --git a/package.json b/package.json index 9abb632b87..e7ac4acb3b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "start:delegation": "turbo start --filter pagopa-interop-delegation-process", "start:delegation-readmodel-writer": "turbo start --filter pagopa-interop-delegation-readmodel-writer", "start:datalake-data-export": "turbo start --filter pagopa-interop-datalake-data-export", + "start:authorization-server": "turbo start --filter pagopa-interop-authorization-server", "test": "turbo test", "build": "turbo build", "check": "turbo check", diff --git a/packages/api-clients/open-api/authorizationServerApi.yml b/packages/api-clients/open-api/authorizationServerApi.yml new file mode 100644 index 0000000000..3fcbf37b1e --- /dev/null +++ b/packages/api-clients/open-api/authorizationServerApi.yml @@ -0,0 +1,215 @@ +openapi: 3.0.3 +info: + title: Interoperability Authorization Server Micro Service + description: Provides endpoints to request an interoperability token + version: "0.1.0" + 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-server" + description: Interoperability Authorization Server +tags: + - name: auth + 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: + "/token.oauth2": + post: + tags: + - auth + summary: Create a new access token + description: Return the generated access token + operationId: createToken + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: "#/components/schemas/AccessTokenRequest" + responses: + "200": + description: The Access token + headers: + Cache-Control: + schema: + type: string + default: no-cache, no-store + description: no-cache, no-store + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/ClientCredentialsResponse" + "400": + description: Bad request + x-noqa: RFC6749 + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "401": + description: Unauthorized + x-noqa: RFC6749 + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + /status: + get: + security: [] + summary: Returns the application status + description: Returns the application status + operationId: get_status + tags: + - health + responses: + "200": + description: This is the valid status from the server. + content: + application/problem+json: + schema: + $ref: "#/components/schemas/Problem" +components: + schemas: + AccessTokenRequest: + type: object + required: + - client_assertion + - client_assertion_type + - grant_type + properties: + client_id: + type: string + example: e58035ce-c753-4f72-b613-46f8a17b71cc + client_assertion: + type: string + format: jws + client_assertion_type: + type: string + example: urn:ietf:params:oauth:client-assertion-type:jwt-bearer + grant_type: + type: string + enum: + - client_credentials + TokenType: + type: string + description: Represents the token type + enum: + - Bearer + ClientCredentialsResponse: + type: object + required: + - access_token + - token_type + - expires_in + properties: + access_token: + type: string + format: jws + token_type: + $ref: "#/components/schemas/TokenType" + expires_in: + type: integer + format: int32 + maximum: 600 + 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: 400 + 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: 0 + 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 diff --git a/packages/api-clients/src/index.ts b/packages/api-clients/src/index.ts index 2a5174bbba..cc32f1e16f 100644 --- a/packages/api-clients/src/index.ts +++ b/packages/api-clients/src/index.ts @@ -11,3 +11,4 @@ export * as apiGatewayApi from "./apiGatewayApi.js"; export * as notifierApi from "./generated/notifierApi.js"; export * as delegationApi from "./generated/delegationApi.js"; export * from "./selfcareClients.js"; +export * as authorizationServerApi from "./generated/authorizationServerApi.js"; diff --git a/packages/authorization-platformstate-writer/test/sample.integration.test.ts b/packages/authorization-platformstate-writer/test/sample.integration.test.ts deleted file mode 100644 index a4e96fa400..0000000000 --- a/packages/authorization-platformstate-writer/test/sample.integration.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -/* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; -import { - afterAll, - afterEach, - beforeAll, - beforeEach, - describe, - expect, - it, - vi, -} from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { - buildDynamoDBTables, - deleteDynamoDBTables, -} from "pagopa-interop-commons-test"; -import { config } from "./utils.js"; - -describe("integration tests V2 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); - beforeEach(async () => { - await buildDynamoDBTables(dynamoDBClient); - }); - afterEach(async () => { - await deleteDynamoDBTables(dynamoDBClient); - }); - const mockDate = new Date(); - beforeAll(() => { - vi.useFakeTimers(); - vi.setSystemTime(mockDate); - }); - afterAll(() => { - vi.useRealTimers(); - }); - - it("sample", () => { - expect(1).toBe(1); - }); -}); diff --git a/packages/authorization-server/.env b/packages/authorization-server/.env new file mode 100644 index 0000000000..eec5e7bf87 --- /dev/null +++ b/packages/authorization-server/.env @@ -0,0 +1,40 @@ +HOST=0.0.0.0 +PORT=3300 +LOG_LEVEL=info + +AWS_CONFIG_FILE=aws.config.local +TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" +TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" + +AWS_REGION="eu-south-1" + +CLIENT_ASSERTION_AUDIENCE="test.interop.pagopa.it" + +GENERATED_INTEROP_TOKEN_ALGORITHM="RS256" +GENERATED_INTEROP_TOKEN_KID="ffcc9b5b-4612-49b1-9374-9d203a3834f2" +GENERATED_INTEROP_TOKEN_ISSUER="test" +GENERATED_INTEROP_TOKEN_M2M_AUDIENCE="test.interop.pagopa.it" +GENERATED_INTEROP_TOKEN_M2M_DURATION_SECONDS=60 +TOKEN_AUDITING_TOPIC="authorization-server.generated-jwt" + +RATE_LIMITER_BURST_PERCENTAGE="0" +RATE_LIMITER_MAX_REQUESTS="10" +RATE_LIMITER_RATE_INTERVAL_MILLIS="1000" +RATE_LIMITER_REDIS_HOST="localhost" +RATE_LIMITER_REDIS_PORT="6379" +RATE_LIMITER_TIMEOUT_MILLIS="300" + +PRODUCER_KAFKA_CLIENT_ID="authorization-server" +PRODUCER_KAFKA_BROKERS="localhost:9092" +PRODUCER_KAFKA_DISABLE_AWS_IAM_AUTH="true" + +S3_BUCKET=interop-local-bucket +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +KAFKA_CLIENT_ID="authorization-server" +KAFKA_GROUP_ID="authorization-server-group" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" + diff --git a/packages/authorization-server/Dockerfile b/packages/authorization-server/Dockerfile new file mode 100644 index 0000000000..6c1c4d89ea --- /dev/null +++ b/packages/authorization-server/Dockerfile @@ -0,0 +1,51 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/authorization-server/package.json /app/packages/authorization-server/package.json +COPY ./packages/commons/package.json /app/packages/commons/package.json +COPY ./packages/models/package.json /app/packages/models/package.json +COPY ./packages/client-assertion-validation/package.json /app/packages/client-assertion-validation/package.json +COPY ./packages/kafka-iam-auth/package.json /app/packages/kafka-iam-auth/package.json +COPY ./packages/api-clients/package.json /app/packages/api-clients/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-server /app/packages/authorization-server +COPY ./packages/commons /app/packages/commons +COPY ./packages/models /app/packages/models +COPY ./packages/client-assertion-validation /app/packages/client-assertion-validation +COPY ./packages/kafka-iam-auth /app/packages/kafka-iam-auth +COPY ./packages/api-clients /app/packages/api-clients + +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-server/node_modules \ + package*.json packages/authorization-server/package*.json \ + packages/commons/ \ + packages/models/ \ + packages/client-assertion-validation/ \ + packages/kafka-iam-auth/ \ + packages/api-clients \ + packages/authorization-server/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/authorization-server +EXPOSE 3300 + +CMD [ "node", "." ] diff --git a/packages/authorization-server/aws.config.local b/packages/authorization-server/aws.config.local new file mode 100644 index 0000000000..042982b52d --- /dev/null +++ b/packages/authorization-server/aws.config.local @@ -0,0 +1,12 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 +services=local + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 + +kms= + endpoint_url=http://localhost:4566 diff --git a/packages/authorization-server/package.json b/packages/authorization-server/package.json new file mode 100644 index 0000000000..0fd0589788 --- /dev/null +++ b/packages/authorization-server/package.json @@ -0,0 +1,53 @@ +{ + "name": "pagopa-interop-authorization-server", + "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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@protobuf-ts/runtime": "2.9.4", + "@types/express": "4.17.21", + "@types/node": "20.14.6", + "@types/uuid": "9.0.8", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0", + "uuid": "10.0.0", + "jose": "5.9.4" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "3.637.0", + "@aws-sdk/client-kms": "3.600.0", + "@aws-sdk/util-dynamodb": "3.637.0", + "@zodios/core": "10.9.6", + "@zodios/express": "10.6.1", + "axios": "1.7.4", + "connection-string": "4.4.0", + "dotenv-flow": "4.1.0", + "express": "4.20.0", + "kafka-iam-auth": "workspace:*", + "openapi-zod-client": "1.18.1", + "pagopa-interop-api-clients": "workspace:*", + "pagopa-interop-client-assertion-validation": "workspace:*", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/authorization-server/src/app.ts b/packages/authorization-server/src/app.ts new file mode 100644 index 0000000000..31e2d6bca3 --- /dev/null +++ b/packages/authorization-server/src/app.ts @@ -0,0 +1,24 @@ +import { + contextMiddleware, + loggerMiddleware, + zodiosCtx, +} from "pagopa-interop-commons"; +import express from "express"; +import healthRouter from "./routers/HealthRouter.js"; +import authorizationServerRouter from "./routers/AuthorizationServerRouter.js"; + +const serviceName = "authorization-server"; + +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(healthRouter); +app.use(contextMiddleware(serviceName, false)); +app.use(express.urlencoded({ extended: true })); +app.use(loggerMiddleware(serviceName)); +app.use(authorizationServerRouter(zodiosCtx)); + +export default app; diff --git a/packages/authorization-server/src/config/config.ts b/packages/authorization-server/src/config/config.ts new file mode 100644 index 0000000000..8e81b097a3 --- /dev/null +++ b/packages/authorization-server/src/config/config.ts @@ -0,0 +1,43 @@ +import { + FileManagerConfig, + KafkaProducerConfig, + RedisRateLimiterConfig, + S3Config, + AuthorizationServerTokenGenerationConfig, + HTTPServerConfig, + LoggerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const AuthorizationServerConfig = HTTPServerConfig.and(LoggerConfig) + .and(RedisRateLimiterConfig) + .and(KafkaProducerConfig) + .and(FileManagerConfig) + .and(S3Config) + .and( + z + .object({ + TOKEN_AUDITING_TOPIC: z.string(), + }) + .transform((c) => ({ + tokenAuditingTopic: c.TOKEN_AUDITING_TOPIC, + })) + ) + .and(AuthorizationServerTokenGenerationConfig) + .and( + z + .object({ + TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION: z.string(), + }) + .transform((c) => ({ + tokenGenerationStatesTable: + c.TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION, + })) + ); + +export type AuthorizationServerConfig = z.infer< + typeof AuthorizationServerConfig +>; + +export const config: AuthorizationServerConfig = + AuthorizationServerConfig.parse(process.env); diff --git a/packages/authorization-server/src/index.ts b/packages/authorization-server/src/index.ts new file mode 100644 index 0000000000..160d2c4711 --- /dev/null +++ b/packages/authorization-server/src/index.ts @@ -0,0 +1,7 @@ +import { genericLogger } from "pagopa-interop-commons"; +import { config } from "./config/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-server/src/model/domain/errors.ts b/packages/authorization-server/src/model/domain/errors.ts new file mode 100644 index 0000000000..f263765cb1 --- /dev/null +++ b/packages/authorization-server/src/model/domain/errors.ts @@ -0,0 +1,124 @@ +import { + ApiError, + ClientId, + ClientKindTokenStates, + makeApiProblemBuilder, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, +} from "pagopa-interop-models"; + +export const errorCodes = { + clientAssertionRequestValidationFailed: "0001", + clientAssertionValidationFailed: "0002", + clientAssertionSignatureValidationFailed: "0003", + kafkaAuditingFailed: "0004", + fallbackAuditFailed: "0005", + tokenGenerationStatesEntryNotFound: "0006", + invalidTokenClientKidPurposeEntry: "0007", + keyTypeMismatch: "0008", + unexpectedTokenGenerationStatesEntry: "0009", + platformStateValidationFailed: "0010", +}; + +export type ErrorCodes = keyof typeof errorCodes; + +export const makeApiProblem = makeApiProblemBuilder(errorCodes); + +export function clientAssertionRequestValidationFailed( + clientId: string | undefined +): ApiError { + return new ApiError({ + detail: `Client assertion request validation failed for request by client ${clientId}`, + code: "clientAssertionRequestValidationFailed", + title: "Client assertion request validation failed", + }); +} + +export function clientAssertionValidationFailed( + clientId: string | undefined +): ApiError { + return new ApiError({ + detail: `Client assertion validation failed for clientId: ${clientId}`, + code: "clientAssertionValidationFailed", + title: "Client assertion validation failed", + }); +} + +export function clientAssertionSignatureValidationFailed( + clientId: string | undefined +): ApiError { + return new ApiError({ + detail: `Client assertion signature validation failed for client ${clientId}`, + code: "clientAssertionSignatureValidationFailed", + title: "Client assertion signature validation failed", + }); +} + +export function kafkaAuditingFailed(): ApiError { + return new ApiError({ + detail: "Kafka auditing failed ", + code: "kafkaAuditingFailed", + title: "Kafka auditing failed", + }); +} + +export function fallbackAuditFailed(clientId: ClientId): ApiError { + return new ApiError({ + detail: `Fallback audit failed for client ${clientId}`, + code: "fallbackAuditFailed", + title: "Fallback audit failed", + }); +} + +export function tokenGenerationStatesEntryNotFound( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): ApiError { + return new ApiError({ + detail: `Entry with PK ${pk} not found in token-generation-states table`, + code: "tokenGenerationStatesEntryNotFound", + title: "token-generation-states entry not found", + }); +} + +export function invalidTokenClientKidPurposeEntry( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): ApiError { + return new ApiError({ + detail: `Missing data in client-kid-purpose entry from token-generation-states table. Primary key: ${pk}`, + code: "invalidTokenClientKidPurposeEntry", + title: "Invalid token client-kid-purpose entry", + }); +} + +export function keyTypeMismatch( + pk: + | TokenGenerationStatesClientKidPurposePK + | TokenGenerationStatesClientKidPK, + clientKind: ClientKindTokenStates +): ApiError { + return new ApiError({ + detail: `Token-generation entry ${pk} can't have client kind: ${clientKind}`, + code: "keyTypeMismatch", + title: "Key type mismatch", + }); +} + +export function unexpectedTokenGenerationStatesEntry( + pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK +): ApiError { + return new ApiError({ + detail: `Unexpected token-generation-states entry, primary key: ${pk}`, + code: "unexpectedTokenGenerationStatesEntry", + title: "Unexpected token-generation-states entry", + }); +} + +export function platformStateValidationFailed( + details: string[] +): ApiError { + return new ApiError({ + detail: `Platform state validation failed - reasons: ${details}`, + code: "platformStateValidationFailed", + title: "Platform state validation failed", + }); +} diff --git a/packages/authorization-server/src/model/domain/models.ts b/packages/authorization-server/src/model/domain/models.ts new file mode 100644 index 0000000000..09fefba9f0 --- /dev/null +++ b/packages/authorization-server/src/model/domain/models.ts @@ -0,0 +1,5 @@ +export interface InteropTokenResponse { + access_token: string; + token_type: string; + expires_in: number; +} diff --git a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts new file mode 100644 index 0000000000..ec49766192 --- /dev/null +++ b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts @@ -0,0 +1,104 @@ +import { + ExpressContext, + fromAppContext, + initFileManager, + initRedisRateLimiter, + InteropTokenGenerator, + rateLimiterHeadersFromStatus, + ZodiosContext, + zodiosValidationErrorToApiProblem, +} from "pagopa-interop-commons"; +import { tooManyRequestsError } from "pagopa-interop-models"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { initProducer } from "kafka-iam-auth"; +import { ZodiosEndpointDefinitions } from "@zodios/core"; +import { ZodiosRouter } from "@zodios/express"; +import { makeApiProblem } from "../model/domain/errors.js"; +import { authorizationServerErrorMapper } from "../utilities/errorMappers.js"; +import { tokenServiceBuilder } from "../services/tokenService.js"; +import { config } from "../config/config.js"; + +const dynamoDBClient = new DynamoDBClient({}); +const redisRateLimiter = await initRedisRateLimiter({ + limiterGroup: "AUTHSERVER", + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + burstPercentage: config.rateLimiterBurstPercentage, + redisHost: config.rateLimiterRedisHost, + redisPort: config.rateLimiterRedisPort, + timeout: config.rateLimiterTimeout, +}); +const producer = await initProducer(config, config.tokenAuditingTopic); +const fileManager = initFileManager(config); + +const tokenGenerator = new InteropTokenGenerator({ + generatedInteropTokenKid: config.generatedInteropTokenKid, + generatedInteropTokenIssuer: config.generatedInteropTokenIssuer, + generatedInteropTokenM2MAudience: config.generatedInteropTokenM2MAudience, + generatedInteropTokenM2MDurationSeconds: + config.generatedInteropTokenM2MDurationSeconds, +}); + +const tokenService = tokenServiceBuilder({ + tokenGenerator, + dynamoDBClient, + redisRateLimiter, + producer, + fileManager, +}); + +const authorizationServerRouter = ( + ctx: ZodiosContext +): ZodiosRouter => { + const authorizationServerRouter = ctx.router( + authorizationServerApi.authApi.api, + { + validationErrorHandler: zodiosValidationErrorToApiProblem, + } + ); + authorizationServerRouter.post("/token.oauth2", async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + const tokenResult = await tokenService.generateToken( + req.body, + ctx.correlationId, + ctx.logger + ); + + const headers = rateLimiterHeadersFromStatus( + tokenResult.rateLimiterStatus + ); + res.set(headers); + + if (tokenResult.limitReached) { + const errorRes = makeApiProblem( + tooManyRequestsError(tokenResult.rateLimitedTenantId), + authorizationServerErrorMapper, + ctx.logger, + ctx.correlationId + ); + + return res.status(errorRes.status).send(errorRes); + } + + return res.status(200).send({ + access_token: tokenResult.token.serialized, + token_type: "Bearer", + expires_in: tokenResult.token.payload.exp, + }); + } catch (err) { + const errorRes = makeApiProblem( + err, + authorizationServerErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + }); + return authorizationServerRouter; +}; + +export default authorizationServerRouter; diff --git a/packages/authorization-server/src/routers/HealthRouter.ts b/packages/authorization-server/src/routers/HealthRouter.ts new file mode 100644 index 0000000000..6658f9ab36 --- /dev/null +++ b/packages/authorization-server/src/routers/HealthRouter.ts @@ -0,0 +1,8 @@ +import { zodiosRouter } from "@zodios/express"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; + +const healthRouter = zodiosRouter(authorizationServerApi.healthApi.api); + +healthRouter.get("/status", async (_, res) => res.status(200).send()); + +export default healthRouter; diff --git a/packages/authorization-server/src/services/tokenService.ts b/packages/authorization-server/src/services/tokenService.ts new file mode 100644 index 0000000000..e582016a03 --- /dev/null +++ b/packages/authorization-server/src/services/tokenService.ts @@ -0,0 +1,412 @@ +import { + validateClientKindAndPlatformState, + validateRequestParameters, + verifyClientAssertion, + verifyClientAssertionSignature, +} from "pagopa-interop-client-assertion-validation"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { + clientKidPrefix, + clientKidPurposePrefix, + clientKindTokenStates, + DescriptorId, + EServiceId, + generateId, + genericInternalError, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericEntry, + unsafeBrandId, + GeneratedTokenAuditDetails, + GSIPKEServiceIdDescriptorId, + ClientAssertion, + FullTokenGenerationStatesClientPurposeEntry, +} from "pagopa-interop-models"; +import { + DynamoDBClient, + GetItemCommand, + GetItemCommandOutput, + GetItemInput, +} from "@aws-sdk/client-dynamodb"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { match } from "ts-pattern"; +import { + FileManager, + formatDateyyyyMMdd, + formatTimehhmmss, + InteropApiToken, + InteropConsumerToken, + InteropTokenGenerator, + Logger, + RateLimiter, + RateLimiterStatus, +} from "pagopa-interop-commons"; +import { initProducer } from "kafka-iam-auth"; +import { config } from "../config/config.js"; +import { + clientAssertionRequestValidationFailed, + clientAssertionSignatureValidationFailed, + clientAssertionValidationFailed, + fallbackAuditFailed, + invalidTokenClientKidPurposeEntry, + kafkaAuditingFailed, + tokenGenerationStatesEntryNotFound, + keyTypeMismatch, + unexpectedTokenGenerationStatesEntry, + platformStateValidationFailed, +} from "../model/domain/errors.js"; + +export type GenerateTokenReturnType = + | { + limitReached: true; + token: undefined; + rateLimitedTenantId: TenantId; + rateLimiterStatus: Omit; + } + | { + limitReached: false; + token: InteropConsumerToken | InteropApiToken; + rateLimiterStatus: Omit; + }; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function tokenServiceBuilder({ + tokenGenerator, + dynamoDBClient, + redisRateLimiter, + producer, + fileManager, +}: { + tokenGenerator: InteropTokenGenerator; + dynamoDBClient: DynamoDBClient; + redisRateLimiter: RateLimiter; + producer: Awaited>; + fileManager: FileManager; +}) { + return { + async generateToken( + request: authorizationServerApi.AccessTokenRequest, + correlationId: string, + logger: Logger + ): Promise { + const { errors: parametersErrors } = validateRequestParameters({ + client_assertion: request.client_assertion, + client_assertion_type: request.client_assertion_type, + grant_type: request.grant_type, + client_id: request.client_id, + }); + + if (parametersErrors) { + throw clientAssertionRequestValidationFailed(request.client_id); + } + + const { data: jwt, errors: clientAssertionErrors } = + verifyClientAssertion(request.client_assertion, request.client_id); + + if (clientAssertionErrors) { + // TODO double check if errors have to be logged or put inside the error below (check the same for parameters errors) + logger.warn(clientAssertionErrors.map((error) => error.detail)); + throw clientAssertionValidationFailed(request.client_id); + } + + const clientId = jwt.payload.sub; + const kid = jwt.header.kid; + const purposeId = jwt.payload.purposeId; + + const pk = purposeId + ? makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }) + : makeTokenGenerationStatesClientKidPK({ clientId, kid }); + + const key = await retrieveKey(dynamoDBClient, pk); + + const { errors: clientAssertionSignatureErrors } = + await verifyClientAssertionSignature( + request.client_assertion, + key, + jwt.header.alg + ); + + if (clientAssertionSignatureErrors) { + throw clientAssertionSignatureValidationFailed(request.client_id); + } + + const { errors: platformStateErrors } = + validateClientKindAndPlatformState(key, jwt); + if (platformStateErrors) { + throw platformStateValidationFailed( + platformStateErrors.map((error) => error.detail) + ); + } + + const { limitReached, ...rateLimiterStatus } = + await redisRateLimiter.rateLimitByOrganization(key.consumerId, logger); + if (limitReached) { + return { + limitReached: true, + token: undefined, + rateLimitedTenantId: key.consumerId, + rateLimiterStatus, + }; + } + + return await match(key.clientKind) + .with(clientKindTokenStates.consumer, async () => { + const parsedKey = + FullTokenGenerationStatesClientPurposeEntry.safeParse(key); + if (parsedKey.success) { + const token = await tokenGenerator.generateInteropConsumerToken({ + sub: jwt.payload.sub, + audience: parsedKey.data.descriptorAudience, + purposeId: parsedKey.data.GSIPK_purposeId, + tokenDurationInSeconds: parsedKey.data.descriptorVoucherLifespan, + digest: jwt.payload.digest, + }); + + await publishAudit({ + producer, + generatedToken: token, + key: parsedKey.data, + clientAssertion: jwt, + correlationId, + fileManager, + logger, + }); + + return { + limitReached: false as const, + token, + rateLimiterStatus, + }; + } + throw invalidTokenClientKidPurposeEntry(key.PK); + }) + .with(clientKindTokenStates.api, async () => { + const token = await tokenGenerator.generateInteropApiToken({ + sub: jwt.payload.sub, + consumerId: key.consumerId, + }); + + return { + limitReached: false as const, + token, + rateLimiterStatus, + }; + }) + .exhaustive(); + }, + }; +} + +export const retrieveKey = async ( + dynamoDBClient: DynamoDBClient, + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): Promise< + TokenGenerationStatesClientEntry | TokenGenerationStatesClientPurposeEntry +> => { + const input: GetItemInput = { + Key: { + PK: { S: pk }, + }, + TableName: config.tokenGenerationStatesTable, + }; + + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); + + if (!data.Item) { + throw tokenGenerationStatesEntryNotFound(pk); + } else { + const unmarshalled = unmarshall(data.Item); + const tokenGenerationEntry = + TokenGenerationStatesGenericEntry.safeParse(unmarshalled); + + if (!tokenGenerationEntry.success) { + throw genericInternalError( + `Unable to parse token generation entry item: result ${JSON.stringify( + tokenGenerationEntry + )} - data ${JSON.stringify(data)} ` + ); + } + + return match(tokenGenerationEntry.data) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.consumer && + entry.PK.startsWith(clientKidPurposePrefix), + () => { + const clientKidPurposeEntry = + FullTokenGenerationStatesClientPurposeEntry.safeParse( + tokenGenerationEntry.data + ); + if (!clientKidPurposeEntry.success) { + throw invalidTokenClientKidPurposeEntry( + tokenGenerationEntry.data.PK + ); + } + + return clientKidPurposeEntry.data; + } + ) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.consumer && + entry.PK.startsWith(clientKidPrefix), + (entry) => { + throw keyTypeMismatch(entry.PK, entry.clientKind); + } + ) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.api && + entry.PK.startsWith(clientKidPurposePrefix), + (entry) => { + throw keyTypeMismatch(entry.PK, entry.clientKind); + } + ) + .when( + (entry) => + entry.clientKind === clientKindTokenStates.api && + entry.PK.startsWith(clientKidPrefix), + () => tokenGenerationEntry.data as TokenGenerationStatesClientEntry + ) + .otherwise(() => { + throw unexpectedTokenGenerationStatesEntry( + tokenGenerationEntry.data.PK + ); + }); + } +}; + +export const publishAudit = async ({ + producer, + generatedToken, + key, + clientAssertion, + correlationId, + fileManager, + logger, +}: { + producer: Awaited>; + generatedToken: InteropConsumerToken | InteropApiToken; + key: FullTokenGenerationStatesClientPurposeEntry; + clientAssertion: ClientAssertion; + correlationId: string; + fileManager: FileManager; + logger: Logger; +}): Promise => { + const messageBody: GeneratedTokenAuditDetails = { + jwtId: generatedToken.payload.jti, + correlationId, + issuedAt: generatedToken.payload.iat, + clientId: clientAssertion.payload.sub, + organizationId: key.consumerId, + agreementId: key.agreementId, + eserviceId: deconstructGSIPK_eserviceId_descriptorId( + key.GSIPK_eserviceId_descriptorId + ).eserviceId, + descriptorId: deconstructGSIPK_eserviceId_descriptorId( + key.GSIPK_eserviceId_descriptorId + ).descriptorId, + purposeId: key.GSIPK_purposeId, + purposeVersionId: unsafeBrandId(key.purposeVersionId), + algorithm: generatedToken.header.alg, + keyId: generatedToken.header.kid, + audience: generatedToken.payload.aud.join(","), + subject: generatedToken.payload.sub, + notBefore: generatedToken.payload.nbf, + expirationTime: generatedToken.payload.exp, + issuer: generatedToken.payload.iss, + clientAssertion: { + algorithm: clientAssertion.header.alg, + audience: [clientAssertion.payload.aud].flat().join(","), + expirationTime: clientAssertion.payload.exp, + issuedAt: clientAssertion.payload.iat, + issuer: clientAssertion.payload.iss, + jwtId: clientAssertion.payload.jti, + keyId: clientAssertion.header.kid, + subject: clientAssertion.payload.sub, + }, + }; + + try { + const res = await producer.send({ + messages: [ + { + key: generatedToken.payload.jti, + value: JSON.stringify(messageBody), + }, + ], + }); + if (res.length === 0 || res[0].errorCode !== 0) { + throw kafkaAuditingFailed(); + } + } catch (e) { + logger.info("main auditing flow failed, going through fallback"); + await fallbackAudit(messageBody, fileManager, logger); + } +}; + +export const fallbackAudit = async ( + messageBody: GeneratedTokenAuditDetails, + fileManager: FileManager, + logger: Logger +): Promise => { + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + const hmsTime = formatTimehhmmss(date); + + const fileName = `${ymdDate}_${hmsTime}_${generateId()}.ndjson`; + const filePath = `token-details/${ymdDate}`; + + try { + await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: filePath, + name: fileName, + content: Buffer.from(JSON.stringify(messageBody)), + }, + logger + ); + logger.info("auditing succeeded through fallback"); + } catch { + throw fallbackAuditFailed(messageBody.clientId); + } +}; + +const deconstructGSIPK_eserviceId_descriptorId = ( + gsi: GSIPKEServiceIdDescriptorId +): { eserviceId: EServiceId; descriptorId: DescriptorId } => { + const substrings = gsi.split("#"); + const eserviceId = substrings[0]; + const descriptorId = substrings[1]; + const parsedEserviceId = EServiceId.safeParse(eserviceId); + + if (!parsedEserviceId.success) { + throw genericInternalError( + `Unable to parse extract eserviceId from GSIPKEServiceIdDescriptorId: ${GSIPKEServiceIdDescriptorId}` + ); + } + + const parsedDescriptorId = DescriptorId.safeParse(descriptorId); + + if (!parsedDescriptorId.success) { + throw genericInternalError( + `Unable to parse extract descriptorId from GSIPKEServiceIdDescriptorId: ${GSIPKEServiceIdDescriptorId}` + ); + } + + return { + eserviceId: parsedEserviceId.data, + descriptorId: parsedDescriptorId.data, + }; +}; diff --git a/packages/authorization-server/src/utilities/errorMappers.ts b/packages/authorization-server/src/utilities/errorMappers.ts new file mode 100644 index 0000000000..f97666fb03 --- /dev/null +++ b/packages/authorization-server/src/utilities/errorMappers.ts @@ -0,0 +1,23 @@ +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_BAD_REQUEST } = + constants; + +export const authorizationServerErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "tokenGenerationStatesEntryNotFound", + "clientAssertionRequestValidationFailed", + "clientAssertionSignatureValidationFailed", + "clientAssertionValidationFailed", + "platformStateValidationFailed", + () => HTTP_STATUS_BAD_REQUEST + ) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/authorization-server/test/.eslintrc.json b/packages/authorization-server/test/.eslintrc.json new file mode 100644 index 0000000000..6135a5ce08 --- /dev/null +++ b/packages/authorization-server/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-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts new file mode 100644 index 0000000000..158b500d10 --- /dev/null +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -0,0 +1,808 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import crypto from "crypto"; +import { fail } from "assert"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockTokenStatesClientPurposeEntry, + getMockPurpose, + getMockPurposeVersion, + getMockTokenStatesClientEntry, + writeTokenStateEntry, + writeTokenStateClientEntry, + getMockClientAssertion, +} from "pagopa-interop-commons-test"; +import { + AgreementId, + ClientId, + clientKindTokenStates, + EServiceId, + GeneratedTokenAuditDetails, + generateId, + itemState, + makeGSIPKKid, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + Purpose, + PurposeId, + purposeVersionState, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + unsafeBrandId, +} from "pagopa-interop-models"; +import { formatDateyyyyMMdd, genericLogger } from "pagopa-interop-commons"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { config } from "../src/config/config.js"; +import { + clientAssertionRequestValidationFailed, + clientAssertionSignatureValidationFailed, + clientAssertionValidationFailed, + fallbackAuditFailed, + invalidTokenClientKidPurposeEntry, + keyTypeMismatch, + platformStateValidationFailed, + tokenGenerationStatesEntryNotFound, +} from "../src/model/domain/errors.js"; +import { inactiveEService } from "../../client-assertion-validation/dist/errors.js"; +import { + configTokenGenerationStates, + dynamoDBClient, + fileManager, + getMockAccessTokenRequest, + mockKMSClient, + mockProducer, + tokenService, +} from "./utils.js"; + +describe("authorization server tests", () => { + if (!configTokenGenerationStates) { + fail(); + } + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + mockKMSClient.send.mockImplementation(async () => ({ + Signature: "mock signature", + })); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + vi.restoreAllMocks(); + }); + + it("should throw clientAssertionRequestValidationFailed", async () => { + const { jws } = await getMockClientAssertion(); + + const clientId = generateId(); + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion_type: "wrong-client-assertion-type", + client_assertion: jws, + client_id: clientId, + }; + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(clientAssertionRequestValidationFailed(clientId)); + }); + + it("should throw clientAssertionValidationFailed", async () => { + const { jws } = await getMockClientAssertion({ + standardClaimsOverride: { iat: undefined }, + }); + + const clientId = generateId(); + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(clientAssertionValidationFailed(clientId)); + }); + + it("should throw tokenGenerationStatesEntryNotFound", async () => { + const purposeId = generateId(); + const clientId = generateId(); + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const entryPK = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid: clientAssertion.header.kid!, + purposeId, + }); + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(tokenGenerationStatesEntryNotFound(entryPK)); + }); + + it("should throw invalidTokenClientKidPurposeEntry", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + agreementId: undefined, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + invalidTokenClientKidPurposeEntry(tokenClientPurposeEntry.PK) + ); + }); + + it("should throw keyTypeMismatch - clientKid entry with consumer kind", async () => { + const clientId = generateId(); + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid: clientAssertion.header.kid!, + }); + + const tokenClientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.consumer, + }; + + await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientKidEntry.PK, clientKindTokenStates.consumer) + ); + }); + + it("should throw keyTypeMismatch - clientKidPurpose entry with api kind", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.api, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientKidPurposeEntry.PK, clientKindTokenStates.api) + ); + }); + + it("should throw clientAssertionSignatureValidationFailed", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion } = await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const splitJws = jws.split("."); + const jwsWithWrongSignature = `${splitJws[0]}.${splitJws[1]}.wrong-singature`; + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jwsWithWrongSignature, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK); + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + clientAssertionSignatureValidationFailed(request.client_id) + ); + }); + + it("should throw platformStateValidationFailed", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + descriptorState: itemState.inactive, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + platformStateValidationFailed([inactiveEService().detail]) + ); + }); + + it("should block the request because of the rate limiter", async () => { + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + // eslint-disable-next-line functional/no-let + for (let i = 0; i < config.rateLimiterMaxRequests; i++) { + const response = await tokenService.generateToken( + request, + generateId(), + genericLogger + ); + expect(response.limitReached).toBe(false); + expect(response.rateLimiterStatus.remainingRequests).toBe( + config.rateLimiterMaxRequests - i - 1 + ); + } + + const responseAfterLimitExceeded = await tokenService.generateToken( + request, + generateId(), + genericLogger + ); + + expect(responseAfterLimitExceeded).toEqual({ + limitReached: true, + rateLimitedTenantId: tokenClientKidPurposeEntry.consumerId, + token: undefined, + rateLimiterStatus: { + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: 0, + }, + }); + }); + + it("should throw error during token signing - consumer key", async () => { + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + mockKMSClient.send.mockImplementationOnce(() => + Promise.resolve({ signature: undefined }) + ); + + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.consumer, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + Error("JWT Signature failed. Empty signature returned") + ); + }); + + it("should throw tokenSigningFailed - api key", async () => { + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + mockKMSClient.send.mockImplementationOnce(() => + Promise.resolve({ signature: undefined }) + ); + + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid: clientAssertion.header.kid!, + }); + + const tokenClientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.api, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError( + Error("JWT Signature failed. Empty signature returned") + ); + }); + + it("should throw fallbackAuditFailed - consumer key - kafka audit failed and fallback audit failed", async () => { + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + mockProducer.send.mockImplementationOnce(async () => Promise.reject()); + vi.spyOn(fileManager, "storeBytes").mockImplementationOnce(() => + Promise.reject() + ); + + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + expect( + tokenService.generateToken(request, generateId(), genericLogger) + ).rejects.toThrowError(fallbackAuditFailed(clientId)); + }); + + it("should succeed - consumer key - kafka audit failed and fallback audit succeeded", async () => { + mockProducer.send.mockImplementation(async () => Promise.reject()); + + const purposeId = generateId(); + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId, + } + ); + + const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + + const fileListBeforeAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListBeforeAudit).toHaveLength(0); + + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + const correlationId = generateId(); + const response = await tokenService.generateToken( + request, + correlationId, + genericLogger + ); + + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + + const fileListAfterAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListAfterAudit).toHaveLength(1); + const file = fileListAfterAudit[0]; + const split = file.split("_"); + expect(split[0]).toEqual(`token-details/${ymdDate}/${ymdDate}`); + + const fileContent = await fileManager.get( + config.s3Bucket, + file, + genericLogger + ); + + const decodedFileContent = new TextDecoder().decode(fileContent); + const parsedDecodedFileContent = JSON.parse(decodedFileContent); + + const expectedMessageBody: GeneratedTokenAuditDetails = { + jwtId: generateId(), + correlationId, + issuedAt: parsedDecodedFileContent.issuedAt, + clientId, + organizationId: tokenClientKidPurposeEntry.consumerId, + agreementId: unsafeBrandId( + tokenClientKidPurposeEntry.agreementId! + ), + eserviceId: unsafeBrandId( + tokenClientKidPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[0] + ), + descriptorId: unsafeBrandId( + tokenClientKidPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[1] + ), + purposeId: tokenClientKidPurposeEntry.GSIPK_purposeId!, + purposeVersionId: tokenClientKidPurposeEntry.purposeVersionId!, + algorithm: "RS256", + keyId: config.generatedInteropTokenKid, + audience: tokenClientKidPurposeEntry.descriptorAudience!.join(","), + subject: clientId, + notBefore: parsedDecodedFileContent.notBefore, + expirationTime: parsedDecodedFileContent.expirationTime, + issuer: config.generatedInteropTokenIssuer, + clientAssertion: { + algorithm: clientAssertion.header.alg, + audience: [clientAssertion.payload.aud].flat().join(","), + expirationTime: clientAssertion.payload.exp!, + issuedAt: clientAssertion.payload.iat!, + issuer: clientAssertion.payload.iss!, + jwtId: clientAssertion.payload.jti!, + keyId: clientAssertion.header.kid!, + subject: unsafeBrandId(clientAssertion.payload.sub!), + }, + }; + expect(parsedDecodedFileContent).toEqual(expectedMessageBody); + expect(response.limitReached).toBe(false); + expect(response.token).toBeDefined(); + expect(response.rateLimiterStatus).toEqual({ + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: config.rateLimiterMaxRequests - 1, + }); + }); + + it("should succeed - consumer key - kafka audit succeeded", async () => { + mockProducer.send.mockImplementationOnce(async () => [ + { topic: config.tokenAuditingTopic, partition: 0, errorCode: 0 }, + ]); + mockKMSClient.send.mockImplementationOnce(async () => ({ + Signature: "mock signature", + })); + + vi.spyOn(mockProducer, "send"); + vi.spyOn(fileManager, "storeBytes"); + + const purpose: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { + sub: clientId, + }, + customClaims: { purposeId: purpose.id }, + }); + + const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId, + kid: clientAssertion.header.kid!, + purposeId: purpose.id, + } + ); + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + consumerId: purpose.consumerId, + GSIPK_purposeId: purpose.id, + purposeState: itemState.active, + purposeVersionId: purpose.versions[0].id, + agreementState: itemState.active, + descriptorState: itemState.active, + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(clientAssertion.header.kid!), + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + + const request = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const uuid = crypto.randomUUID(); + const uuidSpy = vi.spyOn(crypto, "randomUUID"); + uuidSpy.mockReturnValue(uuid); + + const correlationId = generateId(); + const result = await tokenService.generateToken( + request, + correlationId, + genericLogger + ); + + expect(result.token).toBeDefined(); + + expect(result.limitReached).toBe(false); + expect(result.token).toBeDefined(); + expect(result.rateLimiterStatus).toEqual({ + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: config.rateLimiterMaxRequests - 1, + }); + + const fileList = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileList).toHaveLength(0); + expect(fileManager.storeBytes).not.toHaveBeenCalled(); + + const actualMessageSent = mockProducer.send.mock.calls[0][0] + .messages[0] as { key: string; value: string }; + + const parsedAuditSent = JSON.parse(actualMessageSent.value); + + const expectedMessageBody: GeneratedTokenAuditDetails = { + jwtId: generateId(), + correlationId, + issuedAt: parsedAuditSent.issuedAt, + clientId, + organizationId: tokenClientPurposeEntry.consumerId, + agreementId: unsafeBrandId( + tokenClientPurposeEntry.agreementId! + ), + eserviceId: unsafeBrandId( + tokenClientPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[0] + ), + descriptorId: unsafeBrandId( + tokenClientPurposeEntry.GSIPK_eserviceId_descriptorId!.split("#")[1] + ), + purposeId: tokenClientPurposeEntry.GSIPK_purposeId!, + purposeVersionId: tokenClientPurposeEntry.purposeVersionId!, + algorithm: "RS256", + keyId: config.generatedInteropTokenKid, + audience: tokenClientPurposeEntry.descriptorAudience!.join(","), + subject: clientId, + notBefore: parsedAuditSent.notBefore, + expirationTime: parsedAuditSent.expirationTime, + issuer: config.generatedInteropTokenIssuer, + clientAssertion: { + algorithm: clientAssertion.header.alg, + audience: [clientAssertion.payload.aud].flat().join(","), + expirationTime: clientAssertion.payload.exp!, + issuedAt: clientAssertion.payload.iat!, + issuer: clientAssertion.payload.iss!, + jwtId: clientAssertion.payload.jti!, + keyId: clientAssertion.header.kid!, + subject: unsafeBrandId(clientAssertion.payload.sub!), + }, + }; + + expect(parsedAuditSent).toEqual(expectedMessageBody); + }); + + it("should succeed - api key - no audit", async () => { + vi.spyOn(fileManager, "storeBytes"); + + const clientId = generateId(); + + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + }); + + const request: authorizationServerApi.AccessTokenRequest = { + ...(await getMockAccessTokenRequest()), + client_assertion: jws, + client_id: clientId, + }; + + const tokenClientKidK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid: clientAssertion.header.kid!, + }); + + const tokenClientKidEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidK), + clientKind: clientKindTokenStates.api, + publicKey: publicKeyEncodedPem, + }; + + await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + + const fileListBefore = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListBefore).toHaveLength(0); + + const response = await tokenService.generateToken( + request, + generateId(), + genericLogger + ); + + const fileListAfter = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListAfter).toHaveLength(0); + expect(fileManager.storeBytes).not.toHaveBeenCalled(); + + expect(response.limitReached).toBe(false); + expect(response.token).toBeDefined(); + expect(response.rateLimiterStatus).toEqual({ + maxRequests: config.rateLimiterMaxRequests, + rateInterval: config.rateLimiterRateInterval, + remainingRequests: config.rateLimiterMaxRequests - 1, + }); + }); +}); diff --git a/packages/authorization-server/test/authorizationServer.unit.test.ts b/packages/authorization-server/test/authorizationServer.unit.test.ts new file mode 100644 index 0000000000..ddfd05fc33 --- /dev/null +++ b/packages/authorization-server/test/authorizationServer.unit.test.ts @@ -0,0 +1,272 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, + writeTokenStateClientEntry, + writeTokenStateEntry, +} from "pagopa-interop-commons-test"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + ClientId, + clientKindTokenStates, + generateId, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, +} from "pagopa-interop-models"; +import {} from "pagopa-interop-client-assertion-validation"; +import { genericLogger } from "pagopa-interop-commons"; +import { fallbackAudit, retrieveKey } from "../src/services/tokenService.js"; +import { + fallbackAuditFailed, + invalidTokenClientKidPurposeEntry, + keyTypeMismatch, + tokenGenerationStatesEntryNotFound, +} from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { + dynamoDBClient, + fileManager, + getMockAuditMessage, + mockKMSClient, + mockProducer, +} from "./utils.js"; + +describe("unit tests", () => { + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + mockKMSClient.send.mockImplementation(async () => ({ + Signature: "mock signature", + })); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + vi.restoreAllMocks(); + }); + + describe("retrieveKey", () => { + it("should throw tokenGenerationStatesEntryNotFound if the clientKidPurpose entry doesn't exist in token-generation-states", async () => { + const clientId1 = generateId(); + const kid = "kid"; + const purposeId1 = generateId(); + const clientId2 = generateId(); + const purposeId2 = generateId(); + + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: clientId1, + kid, + purposeId: purposeId1, + }); + + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: clientId2, + kid, + purposeId: purposeId2, + }); + + const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1); + + await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + + expect( + retrieveKey(dynamoDBClient, tokenClientKidPurposePK2) + ).rejects.toThrowError( + tokenGenerationStatesEntryNotFound(tokenClientKidPurposePK2) + ); + }); + + it("should throw tokenGenerationStatesEntryNotFound if the clientKid entry doesn't exist in token-generation-states", async () => { + const clientId1 = generateId(); + const kid = "kid"; + const clientId2 = generateId(); + + const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ + clientId: clientId1, + kid, + }); + + const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ + clientId: clientId2, + kid, + }); + + const tokenClientEntry1: TokenGenerationStatesClientEntry = + getMockTokenStatesClientEntry(tokenClientKidPK1); + + await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + + expect( + retrieveKey(dynamoDBClient, tokenClientKidPK2) + ).rejects.toThrowError( + tokenGenerationStatesEntryNotFound(tokenClientKidPK2) + ); + }); + + it("should throw invalidTokenClientKidPurposeEntry - clientKidPurpose entry - consumer key - missing info", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + agreementId: undefined, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + expect( + retrieveKey(dynamoDBClient, tokenClientKidPurposePK) + ).rejects.toThrowError( + invalidTokenClientKidPurposeEntry(tokenClientPurposeEntry.PK) + ); + }); + + it("should succeed - clientKidPurpose entry - consumer key", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.consumer, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + const key = await retrieveKey(dynamoDBClient, tokenClientKidPurposePK); + + expect(key).toEqual(tokenClientPurposeEntry); + }); + + it("should throw keyTypeMismatch - clientKid entry with consumer key", async () => { + const clientId = generateId(); + const kid = "kid"; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }); + + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.consumer, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + expect( + retrieveKey(dynamoDBClient, tokenClientKidPK) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientEntry.PK, clientKindTokenStates.consumer) + ); + }); + + it("should throw keyTypeMismatch - clientKidPurpose entry with api key", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + clientKind: clientKindTokenStates.api, + }; + + await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + expect( + retrieveKey(dynamoDBClient, tokenClientKidPurposePK) + ).rejects.toThrowError( + keyTypeMismatch(tokenClientPurposeEntry.PK, clientKindTokenStates.api) + ); + }); + + it("should succeed - clientKid entry - api key", async () => { + const clientId = generateId(); + const kid = "kid"; + + const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }); + + const tokenClientEntry: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(tokenClientKidPK), + clientKind: clientKindTokenStates.api, + }; + + await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + const key = await retrieveKey(dynamoDBClient, tokenClientKidPK); + + expect(key).toEqual(tokenClientEntry); + }); + }); + + describe("fallbackAudit", () => { + it("should write the audit message to the file storage", async () => { + const mockAuditMessage = getMockAuditMessage(); + + const fileListBeforeAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListBeforeAudit).toHaveLength(0); + + await fallbackAudit(mockAuditMessage, fileManager, genericLogger); + + const fileListAfterAudit = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileListAfterAudit).toHaveLength(1); + + const fileContent = await fileManager.get( + config.s3Bucket, + fileListAfterAudit[0], + genericLogger + ); + + const expectedFileContent = JSON.stringify(mockAuditMessage); + + const decodedFileContent = new TextDecoder().decode(fileContent); + expect(decodedFileContent).toEqual(expectedFileContent); + }); + + it("should throw fallbackAuditFailed in case of unsuccessful file write operation", async () => { + const mockAuditMessage = getMockAuditMessage(); + + mockProducer.send.mockImplementationOnce(async () => Promise.reject()); + vi.spyOn(fileManager, "storeBytes").mockImplementationOnce(() => + Promise.reject() + ); + + expect( + fallbackAudit(mockAuditMessage, fileManager, genericLogger) + ).rejects.toThrowError(fallbackAuditFailed(mockAuditMessage.clientId)); + }); + }); +}); diff --git a/packages/authorization-server/test/tsconfig.json b/packages/authorization-server/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/authorization-server/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/authorization-server/test/utils.ts b/packages/authorization-server/test/utils.ts new file mode 100644 index 0000000000..ecbdd4daf7 --- /dev/null +++ b/packages/authorization-server/test/utils.ts @@ -0,0 +1,127 @@ +import { + getMockClientAssertion, + setupTestContainersVitest, +} from "pagopa-interop-commons-test"; +import { + AgreementId, + ClientId, + DescriptorId, + EServiceId, + GeneratedTokenAuditDetails, + generateId, + PurposeId, + PurposeVersionId, + TenantId, +} from "pagopa-interop-models"; +import { afterEach, inject, vi } from "vitest"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { KMSClient } from "@aws-sdk/client-kms"; +import { initProducer } from "kafka-iam-auth"; +import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { dateToSeconds, InteropTokenGenerator } from "pagopa-interop-commons"; +import { tokenServiceBuilder } from "../src/services/tokenService.js"; +import { config } from "../src/config/config.js"; + +export const configTokenGenerationStates = inject( + "tokenGenerationReadModelConfig" +); + +export const { cleanup, fileManager, redisRateLimiter } = + await setupTestContainersVitest( + undefined, + undefined, + inject("fileManagerConfig"), + undefined, + inject("redisRateLimiterConfig") + ); + +afterEach(cleanup); + +if (configTokenGenerationStates === undefined) { + throw new Error("configTokenGenerationStates is undefined"); +} + +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${configTokenGenerationStates.tokenGenerationReadModelDbPort}`, +}); + +export const mockProducer = { + send: vi.fn(), +}; +export const mockKMSClient = { + send: vi.fn(), +}; + +const tokenGenerator = new InteropTokenGenerator( + { + generatedInteropTokenKid: config.generatedInteropTokenKid, + generatedInteropTokenIssuer: config.generatedInteropTokenIssuer, + generatedInteropTokenM2MAudience: config.generatedInteropTokenM2MAudience, + generatedInteropTokenM2MDurationSeconds: + config.generatedInteropTokenM2MDurationSeconds, + }, + mockKMSClient as unknown as KMSClient +); + +export const tokenService = tokenServiceBuilder({ + tokenGenerator, + dynamoDBClient, + redisRateLimiter, + producer: mockProducer as unknown as Awaited>, + fileManager, +}); + +export const getMockAccessTokenRequest = + async (): Promise => { + const { jws } = await getMockClientAssertion(); + return { + client_id: generateId(), + client_assertion_type: + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: jws, + grant_type: "client_credentials", + }; + }; + +export const getMockAuditMessage = (): GeneratedTokenAuditDetails => { + const correlationId = generateId(); + const eserviceId = generateId(); + const descriptorId = generateId(); + const agreementId = generateId(); + const clientId = generateId(); + const purposeId = generateId(); + const kid = "kid"; + const purposeVersionId = generateId(); + const consumerId = generateId(); + const clientAssertionJti = generateId(); + + return { + correlationId, + eserviceId, + descriptorId, + agreementId, + subject: clientId, + audience: "pagopa.it", + purposeId, + algorithm: "RS256", + clientId, + keyId: kid, + purposeVersionId, + jwtId: generateId(), + issuedAt: dateToSeconds(new Date()), + issuer: "interop jwt issuer", + expirationTime: dateToSeconds(new Date()), + organizationId: consumerId, + notBefore: 0, + clientAssertion: { + subject: clientId, + audience: "pagopa.it", + algorithm: "RS256", + keyId: kid, + jwtId: clientAssertionJti, + issuedAt: dateToSeconds(new Date()), + issuer: consumerId, + expirationTime: dateToSeconds(new Date()), + }, + }; +}; diff --git a/packages/authorization-server/test/vitestGlobalSetup.ts b/packages/authorization-server/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/authorization-server/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/authorization-server/tsconfig.check.json b/packages/authorization-server/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/authorization-server/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/authorization-server/tsconfig.json b/packages/authorization-server/tsconfig.json new file mode 100644 index 0000000000..039e0b4d16 --- /dev/null +++ b/packages/authorization-server/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/packages/authorization-server/vitest.config.ts b/packages/authorization-server/vitest.config.ts new file mode 100644 index 0000000000..d1a4f22cc5 --- /dev/null +++ b/packages/authorization-server/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/packages/backend-for-frontend/.env b/packages/backend-for-frontend/.env index 33fe161b6b..4457ca94c7 100644 --- a/packages/backend-for-frontend/.env +++ b/packages/backend-for-frontend/.env @@ -80,4 +80,4 @@ IMPORT_ESERVICE_PATH="local/eservices-import" PRESIGNED_URL_GET_DURATION_MINUTES=5000 PRESIGNED_URL_PUT_DURATION_MINUTES= 5000 -CLIENT_ASSERTION_AUDIENCE="auth.refactor.dev.interop.pagopa.it/client-assertion" +CLIENT_ASSERTION_AUDIENCE="dev.interop.pagopa.it" diff --git a/packages/backend-for-frontend/src/services/toolService.ts b/packages/backend-for-frontend/src/services/toolService.ts index d286488c7a..d6e851a0ea 100644 --- a/packages/backend-for-frontend/src/services/toolService.ts +++ b/packages/backend-for-frontend/src/services/toolService.ts @@ -2,9 +2,6 @@ import { isAxiosError } from "axios"; import { - ApiKey, - ClientAssertion, - ConsumerKey, FailedValidation, SuccessfulValidation, validateClientKindAndPlatformState, @@ -15,11 +12,21 @@ import { import { AgreementId, ApiError, + ClientAssertion, ClientId, + DescriptorId, EServiceId, + GSIPKKid, ItemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, PurposeId, TenantId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, unsafeBrandId, } from "pagopa-interop-models"; import { WithLogger } from "pagopa-interop-commons"; @@ -96,7 +103,11 @@ export function toolsServiceBuilder(clients: PagoPAInteropBeClients) { : undefined; const { errors: clientAssertionSignatureErrors } = - await verifyClientAssertionSignature(clientAssertion, key); + await verifyClientAssertionSignature( + clientAssertion, + key, + jwt.header.alg + ); if (clientAssertionSignatureErrors) { return handleValidationResults( { @@ -209,7 +220,9 @@ async function retrieveKeyAndEservice( ctx: WithLogger ): Promise< | SuccessfulValidation<{ - key: ApiKey | ConsumerKey; + key: + | TokenGenerationStatesClientEntry + | TokenGenerationStatesClientPurposeEntry; eservice?: catalogApi.EService; descriptor?: catalogApi.EServiceDescriptor; }> @@ -241,26 +254,29 @@ async function retrieveKeyAndEservice( assertIsConsumer(ctx.authData.organizationId, keyWithClient); - const { encodedPem, algorithm } = - await authorizationClient.client.getClientKeyById({ - headers: ctx.headers, - params: { - clientId: keyWithClient.client.id, - keyId: jwt.header.kid, - }, - }); + const { encodedPem } = await authorizationClient.client.getClientKeyById({ + headers: ctx.headers, + params: { + clientId: keyWithClient.client.id, + keyId: jwt.header.kid, + }, + }); if (keyWithClient.client.kind === authorizationApi.ClientKind.enum.API) { return { errors: undefined, data: { key: { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: unsafeBrandId(keyWithClient.client.id), + kid: jwt.header.kid, + }), clientKind: authorizationApi.ClientKind.enum.API, - kid: jwt.header.kid, - algorithm, + GSIPK_kid: unsafeBrandId(jwt.header.kid), publicKey: encodedPem, - clientId: unsafeBrandId(keyWithClient.client.id), + GSIPK_clientId: unsafeBrandId(keyWithClient.client.id), consumerId: unsafeBrandId(keyWithClient.client.consumerId), + updatedAt: new Date().toISOString(), }, }, }; @@ -311,18 +327,36 @@ async function retrieveKeyAndEservice( errors: undefined, data: { key: { + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: unsafeBrandId(keyWithClient.client.id), + kid: jwt.header.kid, + purposeId, + }), clientKind: authorizationApi.ClientKind.enum.CONSUMER, - clientId: unsafeBrandId(keyWithClient.client.id), - kid: jwt.header.kid, - algorithm, + GSIPK_clientId: unsafeBrandId(keyWithClient.client.id), + GSIPK_kid: unsafeBrandId(jwt.header.kid), publicKey: encodedPem, - purposeId, + GSIPK_purposeId: purposeId, consumerId: unsafeBrandId(keyWithClient.client.consumerId), agreementId: unsafeBrandId(agreement.id), - eServiceId: unsafeBrandId(agreement.eserviceId), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: unsafeBrandId(agreement.eserviceId), + descriptorId: unsafeBrandId(agreement.descriptorId), + }), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + eserviceId: unsafeBrandId(agreement.eserviceId), + consumerId: unsafeBrandId(keyWithClient.client.consumerId), + }), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: unsafeBrandId(keyWithClient.client.id), + purposeId, + }), agreementState: agreementStateToItemState(agreement.state), - purposeState: retrievePurposeItemState(purpose), + purposeState: purposeToItemState(purpose), descriptorState: descriptorStateToItemState(descriptor.state), + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + updatedAt: new Date().toISOString(), }, eservice, descriptor, @@ -374,7 +408,7 @@ async function retrieveDescriptor( return descriptor; } -function retrievePurposeItemState(purpose: purposeApi.Purpose): ItemState { +function purposeToItemState(purpose: purposeApi.Purpose): ItemState { const purposeVersion = [...purpose.versions] .sort( (a, b) => @@ -416,10 +450,10 @@ const agreementStateToItemState = ( : ItemState.Enum.INACTIVE; const descriptorStateToItemState = ( - state: catalogApi.EServiceDescriptorState + descriptorState: catalogApi.EServiceDescriptorState ): ItemState => - state === catalogApi.EServiceDescriptorState.Enum.PUBLISHED || - state === catalogApi.EServiceDescriptorState.Enum.DEPRECATED + descriptorState === catalogApi.EServiceDescriptorState.Enum.PUBLISHED || + descriptorState === catalogApi.EServiceDescriptorState.Enum.DEPRECATED ? ItemState.Enum.ACTIVE : ItemState.Enum.INACTIVE; diff --git a/packages/client-assertion-validation/src/errors.ts b/packages/client-assertion-validation/src/errors.ts index 1ba5d7589b..1f1617a1f7 100644 --- a/packages/client-assertion-validation/src/errors.ts +++ b/packages/client-assertion-validation/src/errors.ts @@ -1,55 +1,45 @@ import { ApiError } from "pagopa-interop-models"; export const errorCodes = { - clientAssertionValidationFailure: "0001", - unexpectedClientAssertionSignatureVerificationError: "0002", - invalidAssertionType: "0003", - invalidGrantType: "0004", - invalidAudienceFormat: "0005", - invalidAudience: "0006", - audienceNotFound: "0007", - invalidClientAssertionFormat: "0008", - unexpectedClientAssertionPayload: "0009", - jtiNotFound: "00010", - issuedAtNotFound: "0011", - expNotFound: "0012", - issuerNotFound: "0013", - subjectNotFound: "0014", - invalidSubject: "0015", - invalidPurposeIdClaimFormat: "0016", - kidNotFound: "0017", - clientAssertionSignatureVerificationError: "0018", - tokenExpiredError: "0019", - jsonWebTokenError: "0020", - notBeforeError: "0021", - inactivePurpose: "0022", - inactiveAgreement: "0023", - inactiveEService: "0024", - invalidClientIdFormat: "0025", - invalidSubjectFormat: "0026", - digestClaimNotFound: "0027", - invalidHashLength: "0028", - invalidHashAlgorithm: "0029", - algorithmNotFound: "0030", - algorithmNotAllowed: "0031", - purposeIdNotProvided: "0032", - invalidKidFormat: "0033", - clientAssertionInvalidClaims: "0034", - invalidSignature: "0035", + unexpectedClientAssertionSignatureVerificationError: "0001", + invalidAssertionType: "0002", + invalidGrantType: "0003", + invalidAudienceFormat: "0004", + invalidAudience: "0005", + audienceNotFound: "0006", + invalidClientAssertionFormat: "0007", + unexpectedClientAssertionPayload: "0008", + jtiNotFound: "0009", + issuedAtNotFound: "0010", + expNotFound: "0011", + issuerNotFound: "0012", + subjectNotFound: "0013", + invalidSubject: "0014", + invalidPurposeIdClaimFormat: "0015", + kidNotFound: "0016", + clientAssertionSignatureVerificationError: "0017", + tokenExpiredError: "0018", + jsonWebTokenError: "0019", + notBeforeError: "0020", + inactivePurpose: "0021", + inactiveAgreement: "0022", + inactiveEService: "0023", + invalidClientIdFormat: "0024", + invalidSubjectFormat: "0025", + digestClaimNotFound: "0026", + invalidHashLength: "0027", + invalidHashAlgorithm: "0028", + algorithmNotFound: "0029", + algorithmNotAllowed: "0030", + purposeIdNotProvided: "0031", + invalidKidFormat: "0032", + clientAssertionInvalidClaims: "0033", + invalidSignature: "0034", + missingPlatformStates: "0035", }; export type ErrorCodes = keyof typeof errorCodes; -export function clientAssertionValidationFailure( - details: string -): ApiError { - return new ApiError({ - detail: `Client assertion validation failed: ${details}`, - code: "clientAssertionValidationFailure", - title: "Client assertion validation failed", - }); -} - export function unexpectedClientAssertionSignatureVerificationError( message: string ): ApiError { @@ -336,3 +326,11 @@ export function invalidSignature(): ApiError { title: "Invalid signature", }); } + +export function missingPlatformStates(): ApiError { + return new ApiError({ + detail: "Platform states not available for the entry", + code: "missingPlatformStates", + title: "Missing platform states", + }); +} diff --git a/packages/client-assertion-validation/src/types.ts b/packages/client-assertion-validation/src/types.ts index 38a8729a83..4d9020e990 100644 --- a/packages/client-assertion-validation/src/types.ts +++ b/packages/client-assertion-validation/src/types.ts @@ -1,82 +1,8 @@ -import { - AgreementId, - ApiError, - ClientId, - clientKindTokenStates, - EServiceId, - ItemState, - PurposeId, - TenantId, -} from "pagopa-interop-models"; +import { ApiError } from "pagopa-interop-models"; import { z } from "zod"; import { ErrorCodes } from "./errors.js"; -export const ClientAssertionDigest = z - .object({ - alg: z.string(), - value: z.string(), - }) - .strict(); -export type ClientAssertionDigest = z.infer; - -export const ClientAssertionHeader = z - .object({ - kid: z.string(), - alg: z.string(), - typ: z.string().optional(), - }) - .strict(); -export type ClientAssertionHeader = z.infer; - -export const ClientAssertionPayload = z - .object({ - sub: ClientId, - jti: z.string(), - iat: z.number(), - iss: z.string(), - aud: z.array(z.string()).or(z.string()), - exp: z.number(), - digest: ClientAssertionDigest.optional(), - purposeId: PurposeId.optional(), - }) - .strict(); -export type ClientAssertionPayload = z.infer; - -export const ClientAssertion = z - .object({ - header: ClientAssertionHeader, - payload: ClientAssertionPayload, - }) - .strict(); -export type ClientAssertion = z.infer; - export const Base64Encoded = z.string().base64().min(1); -export const Key = z - .object({ - clientId: ClientId, - consumerId: TenantId, - kid: z.string(), - publicKey: Base64Encoded, - algorithm: z.string(), - }) - .strict(); -export type Key = z.infer; - -export const ConsumerKey = Key.extend({ - clientKind: z.literal(clientKindTokenStates.consumer), - purposeId: PurposeId, - purposeState: ItemState, - agreementId: AgreementId, - agreementState: ItemState, - eServiceId: EServiceId, - descriptorState: ItemState, -}).strict(); -export type ConsumerKey = z.infer; - -export const ApiKey = Key.extend({ - clientKind: z.literal(clientKindTokenStates.api), -}).strict(); -export type ApiKey = z.infer; export type ValidationResult = | SuccessfulValidation diff --git a/packages/client-assertion-validation/src/utils.ts b/packages/client-assertion-validation/src/utils.ts index 6bfa958313..44f16d1692 100644 --- a/packages/client-assertion-validation/src/utils.ts +++ b/packages/client-assertion-validation/src/utils.ts @@ -3,11 +3,11 @@ import { ClientId, itemState, PurposeId, + TokenGenerationStatesClientPurposeEntry, unsafeBrandId, + ClientAssertionDigest, } from "pagopa-interop-models"; import { - ClientAssertionDigest, - ConsumerKey, FailedValidation, ValidationResult, SuccessfulValidation, @@ -190,8 +190,8 @@ export const validateDigest = ( }; export const validatePlatformState = ( - key: ConsumerKey -): ValidationResult => { + key: TokenGenerationStatesClientPurposeEntry +): ValidationResult => { const agreementError = key.agreementState !== itemState.active ? inactiveAgreement() : undefined; diff --git a/packages/client-assertion-validation/src/validation.ts b/packages/client-assertion-validation/src/validation.ts index 6db2e9f4ac..27041566c5 100644 --- a/packages/client-assertion-validation/src/validation.ts +++ b/packages/client-assertion-validation/src/validation.ts @@ -1,5 +1,13 @@ import { match } from "ts-pattern"; -import { clientKindTokenStates } from "pagopa-interop-models"; +import { + clientKidPurposePrefix, + clientKindTokenStates, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, + ClientAssertion, + ClientAssertionHeader, + ClientAssertionPayload, +} from "pagopa-interop-models"; import * as jose from "jose"; import { JOSEError, @@ -29,14 +37,8 @@ import { ALLOWED_ALGORITHM, } from "./utils.js"; import { - ApiKey, Base64Encoded, - ClientAssertion, - ClientAssertionHeader, - ClientAssertionPayload, ClientAssertionValidationRequest, - ConsumerKey, - Key, ValidationResult, } from "./types.js"; import { @@ -53,6 +55,7 @@ import { clientAssertionInvalidClaims, algorithmNotAllowed, clientAssertionSignatureVerificationError, + missingPlatformStates, } from "./errors.js"; export const validateRequestParameters = ( @@ -184,11 +187,14 @@ export const verifyClientAssertion = ( export const verifyClientAssertionSignature = async ( clientAssertionJws: string, - key: Key + key: + | TokenGenerationStatesClientPurposeEntry + | TokenGenerationStatesClientEntry, + clientAssertionAlgorithm: string ): Promise> => { try { - if (key.algorithm !== ALLOWED_ALGORITHM) { - return failedValidation([algorithmNotAllowed(key.algorithm)]); + if (clientAssertionAlgorithm !== ALLOWED_ALGORITHM) { + return failedValidation([algorithmNotAllowed(clientAssertionAlgorithm)]); } if (!Base64Encoded.safeParse(key.publicKey).success) { @@ -211,7 +217,7 @@ export const verifyClientAssertionSignature = async ( const publicKey = createPublicKey(key.publicKey); const result = await jose.jwtVerify(clientAssertionJws, publicKey, { - algorithms: [key.algorithm], + algorithms: [clientAssertionAlgorithm], }); return successfulValidation(result.payload); @@ -241,7 +247,9 @@ export const verifyClientAssertionSignature = async ( }; export const validateClientKindAndPlatformState = ( - key: ApiKey | ConsumerKey, + key: + | TokenGenerationStatesClientEntry + | TokenGenerationStatesClientPurposeEntry, jwt: ClientAssertion ): ValidationResult => match(key) @@ -249,14 +257,18 @@ export const validateClientKindAndPlatformState = ( successfulValidation(jwt) ) .with({ clientKind: clientKindTokenStates.consumer }, (key) => { - const { errors: platformStateErrors } = validatePlatformState(key); - const purposeIdError = jwt.payload.purposeId - ? undefined - : purposeIdNotProvided(); + if (key.PK.startsWith(clientKidPurposePrefix)) { + const parsed = key as TokenGenerationStatesClientPurposeEntry; + const { errors: platformStateErrors } = validatePlatformState(parsed); + const purposeIdError = jwt.payload.purposeId + ? undefined + : purposeIdNotProvided(); - if (!platformStateErrors && !purposeIdError) { - return successfulValidation(jwt); + if (!platformStateErrors && !purposeIdError) { + return successfulValidation(jwt); + } + return failedValidation([platformStateErrors, purposeIdError]); } - return failedValidation([platformStateErrors, purposeIdError]); + return failedValidation([missingPlatformStates()]); }) .exhaustive(); diff --git a/packages/client-assertion-validation/test/utils.ts b/packages/client-assertion-validation/test/utils.ts index 2ad7728e8f..4f35335375 100644 --- a/packages/client-assertion-validation/test/utils.ts +++ b/packages/client-assertion-validation/test/utils.ts @@ -1,19 +1,7 @@ import crypto from "crypto"; -import { - ClientId, - clientKindTokenStates, - generateId, - itemState, - PurposeId, - TenantId, -} from "pagopa-interop-models"; -import * as jose from "jose"; -import { - ApiKey, - ClientAssertionValidationRequest, - ConsumerKey, - Key, -} from ".././src/types.js"; +import { ClientId, generateId } from "pagopa-interop-models"; +import { getMockClientAssertion } from "pagopa-interop-commons-test"; +import { ClientAssertionValidationRequest } from ".././src/types.js"; import { EXPECTED_CLIENT_ASSERTION_TYPE, EXPECTED_CLIENT_CREDENTIALS_GRANT_TYPE, @@ -21,119 +9,6 @@ import { export const value64chars = crypto.randomBytes(32).toString("hex"); -export const getMockClientAssertion = async (props?: { - standardClaimsOverride?: Partial; - customClaims?: { [k: string]: unknown }; - customHeader?: { [k: string]: unknown }; -}): Promise<{ - jws: string; - publicKeyEncodedPem: string; -}> => { - const { keySet, publicKeyEncodedPem } = generateKeySet(); - - const clientId = generateId(); - const defaultPayload: jose.JWTPayload = { - iss: clientId, - sub: clientId, - aud: ["test.interop.pagopa.it", "dev.interop.pagopa.it"], - exp: 60, - jti: generateId(), - iat: 5, - }; - - const actualPayload: jose.JWTPayload = { - ...defaultPayload, - ...props?.standardClaimsOverride, - ...props?.customClaims, - }; - - const headers: jose.JWTHeaderParameters = { - alg: "RS256", - kid: "kid", - ...props?.customHeader, - }; - - const jws = await signClientAssertion({ - payload: actualPayload, - headers, - keySet, - }); - - return { - jws, - publicKeyEncodedPem, - }; -}; - -export const generateKeySet = (): { - keySet: crypto.KeyPairKeyObjectResult; - publicKeyEncodedPem: string; -} => { - const keySet: crypto.KeyPairKeyObjectResult = crypto.generateKeyPairSync( - "rsa", - { - modulusLength: 2048, - } - ); - - const pemPublicKey = keySet.publicKey - .export({ - type: "spki", - format: "pem", - }) - .toString(); - - const publicKeyEncodedPem = Buffer.from(pemPublicKey).toString("base64"); - return { - keySet, - publicKeyEncodedPem, - }; -}; - -const signClientAssertion = async ({ - payload, - headers, - keySet, -}: { - payload: jose.JWTPayload; - headers: jose.JWTHeaderParameters; - keySet: crypto.KeyPairKeyObjectResult; -}): Promise => { - const pemPrivateKey = keySet.privateKey.export({ - type: "pkcs8", - format: "pem", - }); - - const privateKey = crypto.createPrivateKey(pemPrivateKey); - return await new jose.SignJWT(payload) - .setProtectedHeader(headers) - .sign(privateKey); -}; - -export const getMockKey = (): Key => ({ - clientId: generateId(), - consumerId: generateId(), - kid: "kid", - publicKey: generateKeySet().publicKeyEncodedPem, - algorithm: "RS256", -}); - -export const getMockConsumerKey = (): ConsumerKey => ({ - ...getMockKey(), - purposeId: generateId(), - clientKind: clientKindTokenStates.consumer, - purposeState: itemState.active, - agreementId: generateId(), - agreementState: itemState.active, - eServiceId: generateId(), - descriptorState: itemState.active, -}); - -export const getMockApiKey = (): ApiKey => ({ - ...getMockKey(), - clientKind: clientKindTokenStates.api, -}); - export const getMockAccessTokenRequest = async (): Promise => ({ client_id: generateId(), diff --git a/packages/client-assertion-validation/test/validation.test.ts b/packages/client-assertion-validation/test/validation.test.ts index d698ffe1d0..22d1ced539 100644 --- a/packages/client-assertion-validation/test/validation.test.ts +++ b/packages/client-assertion-validation/test/validation.test.ts @@ -3,11 +3,21 @@ import { fail } from "assert"; import { describe, expect, it } from "vitest"; import { ClientId, + clientKindTokenStates, generateId, itemState, PurposeId, + TokenGenerationStatesClientEntry, + TokenGenerationStatesClientPurposeEntry, } from "pagopa-interop-models"; import * as jsonwebtoken from "jsonwebtoken"; +import { + generateKeySet, + getMockClientAssertion, + getMockTokenStatesClientEntry, + getMockTokenStatesClientPurposeEntry, +} from "pagopa-interop-commons-test"; +import { dateToSeconds } from "pagopa-interop-commons"; import { validateClientKindAndPlatformState, validateRequestParameters, @@ -46,21 +56,10 @@ import { clientAssertionInvalidClaims, invalidAudienceFormat, unexpectedClientAssertionSignatureVerificationError, + missingPlatformStates, } from "../src/errors.js"; -import { - ClientAssertionValidationRequest, - ConsumerKey, - Key, -} from "../src/types.js"; -import { - generateKeySet, - getMockAccessTokenRequest, - getMockApiKey, - getMockClientAssertion, - getMockConsumerKey, - getMockKey, - value64chars, -} from "./utils.js"; +import { ClientAssertionValidationRequest } from "../src/types.js"; +import { getMockAccessTokenRequest, value64chars } from "./utils.js"; describe("validation test", async () => { describe("validateRequestParameters", async () => { @@ -488,26 +487,35 @@ describe("validation test", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion({ standardClaimsOverride: { - iat: new Date().getTime() / 1000, - exp: threeHourLater.getTime() / 1000, + iat: dateToSeconds(new Date()), + exp: dateToSeconds(threeHourLater), }, }); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toBeUndefined(); }); it("unexpectedClientAssertionSignatureVerificationError - base64 key expected", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: Buffer.from(publicKeyEncodedPem, "base64").toString("utf8"), }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toHaveLength(1); expect(errors![0]).toEqual( unexpectedClientAssertionSignatureVerificationError( @@ -527,16 +535,20 @@ describe("validation test", async () => { alg: notAllowedAlg, }, standardClaimsOverride: { - iat: new Date().getTime() / 1000, - exp: threeHourLater.getTime() / 1000, + iat: dateToSeconds(new Date()), + exp: dateToSeconds(threeHourLater), }, }); - const mockKey: Key = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, - algorithm: notAllowedAlg, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + notAllowedAlg + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(algorithmNotAllowed(notAllowedAlg)); @@ -551,29 +563,35 @@ describe("validation test", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion({ standardClaimsOverride: { - iat: sixHoursAgo.getTime() / 1000, - exp: threeHourAgo.getTime() / 1000, + iat: dateToSeconds(sixHoursAgo), + exp: dateToSeconds(threeHourAgo), }, }); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(tokenExpiredError()); }); it("jsonWebTokenError", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; + const { errors } = await verifyClientAssertionSignature( "not-a-valid-jws", - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -582,17 +600,19 @@ describe("validation test", async () => { it("invalidSignature", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; + const { jws } = await getMockClientAssertion(); const subStrings = jws.split("."); const clientAssertionWithWrongSignature = `${subStrings[0]}.${subStrings[1]}.wrong-signature`; const { errors } = await verifyClientAssertionSignature( clientAssertionWithWrongSignature, - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -600,13 +620,15 @@ describe("validation test", async () => { }); it("jsonWebTokenError - malformed jwt", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; + const { errors } = await verifyClientAssertionSignature( "too.many.substrings.in.client.assertion", - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -617,8 +639,8 @@ describe("validation test", async () => { const { jws: clientAssertion1, publicKeyEncodedPem } = await getMockClientAssertion(); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; @@ -629,7 +651,8 @@ describe("validation test", async () => { const clientAssertionWithWrongSignature = `${subStrings1[0]}.${subStrings1[1]}.${subStrings2[2]}`; const { errors } = await verifyClientAssertionSignature( clientAssertionWithWrongSignature, - mockKey + mockKey, + "RS256" ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -648,17 +671,21 @@ describe("validation test", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion({ standardClaimsOverride: { - iat: threeHoursAgo.getTime() / 1000, - exp: sixHoursLater.getTime() / 1000, - nbf: threeHoursLater.getTime() / 1000, + iat: dateToSeconds(threeHoursAgo), + exp: dateToSeconds(sixHoursLater), + nbf: dateToSeconds(threeHoursLater), }, }); - const mockKey = { - ...getMockKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), publicKey: publicKeyEncodedPem, }; - const { errors } = await verifyClientAssertionSignature(jws, mockKey); + const { errors } = await verifyClientAssertionSignature( + jws, + mockKey, + "RS256" + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(notBeforeError()); @@ -671,11 +698,8 @@ describe("validation test", async () => { describe("validatePlatformState", async () => { it("success", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), - agreementState: itemState.active, - descriptorState: itemState.active, - purposeState: itemState.active, + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), }; validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -683,8 +707,8 @@ describe("validation test", async () => { }); it("inactiveAgreement", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), agreementState: itemState.inactive, }; validatePlatformState(mockKey); @@ -695,9 +719,11 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactiveAgreement()); }); it("inactiveEservice", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), descriptorState: itemState.inactive, + descriptorAudience: ["test.interop.pagopa.it"], + descriptorVoucherLifespan: 60, }; validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -707,10 +733,11 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactiveEService()); }); it("inactivePurpose", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), purposeState: itemState.inactive, }; + validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -719,8 +746,8 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactivePurpose()); }); it("inactiveAgreement and inactiveEservice and inactivePurpose", async () => { - const mockKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), agreementState: itemState.inactive, descriptorState: itemState.inactive, purposeState: itemState.inactive, @@ -739,8 +766,8 @@ describe("validation test", async () => { }); describe("validateClientKindAndPlatformState", async () => { - it("success (consumerKey with consumer client kind; valid platform states)", async () => { - const mockConsumerKey = getMockConsumerKey(); + it("success (clientKidPurpose entry with consumer client kind; valid platform states)", async () => { + const mockConsumerKey = getMockTokenStatesClientPurposeEntry(); const { data: mockClientAssertion } = verifyClientAssertion( ( await getMockClientAssertion({ @@ -760,8 +787,8 @@ describe("validation test", async () => { }); it("inactiveEService (consumerKey with consumer client kind; invalid platform states)", async () => { - const mockConsumerKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockConsumerKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), descriptorState: itemState.inactive, }; const { data: mockClientAssertion } = verifyClientAssertion( @@ -784,8 +811,11 @@ describe("validation test", async () => { expect(errors![0]).toEqual(inactiveEService()); }); - it("success (apiKey with api client kind)", async () => { - const mockApiKey = getMockApiKey(); + it("success (clientEntry with api client kind)", async () => { + const mockApiKey: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + clientKind: clientKindTokenStates.api, + }; const { data: mockClientAssertion } = verifyClientAssertion( (await getMockClientAssertion()).jws, undefined @@ -800,8 +830,29 @@ describe("validation test", async () => { expect(errors).toBeUndefined(); }); + it("missingPlatformStates (clientEntry with consumer client kind)", async () => { + const mockApiKey: TokenGenerationStatesClientEntry = { + ...getMockTokenStatesClientEntry(), + clientKind: clientKindTokenStates.consumer, + }; + const { data: mockClientAssertion } = verifyClientAssertion( + (await getMockClientAssertion()).jws, + undefined + ); + if (!mockClientAssertion) { + fail(); + } + const { errors } = validateClientKindAndPlatformState( + mockApiKey, + mockClientAssertion + ); + expect(errors).toBeDefined(); + expect(errors).toHaveLength(1); + expect(errors![0]).toEqual(missingPlatformStates()); + }); + it("purposeIdNotProvided for Client Kind Consumer", async () => { - const mockConsumerKey = getMockConsumerKey(); + const mockConsumerKey = getMockTokenStatesClientPurposeEntry(); const { data: mockClientAssertion } = verifyClientAssertion( ( await getMockClientAssertion({ @@ -823,8 +874,8 @@ describe("validation test", async () => { }); it("purposeIdNotProvided and platformStateError", async () => { - const mockConsumerKey: ConsumerKey = { - ...getMockConsumerKey(), + const mockConsumerKey: TokenGenerationStatesClientPurposeEntry = { + ...getMockTokenStatesClientPurposeEntry(), agreementState: itemState.inactive, }; const { data: mockClientAssertion } = verifyClientAssertion( diff --git a/packages/commons-test/package.json b/packages/commons-test/package.json index 55e91be360..d474e20cba 100644 --- a/packages/commons-test/package.json +++ b/packages/commons-test/package.json @@ -31,6 +31,7 @@ "aws-sdk-client-mock": "4.0.1", "axios": "1.7.4", "dotenv-flow": "4.1.0", + "jose": "5.9.4", "jsonwebtoken": "9.0.2", "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index dbb0143273..2d8a8d7692 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -1,3 +1,4 @@ +import crypto from "crypto"; import { fail } from "assert"; import { generateMock } from "@anatine/zod-mock"; import { @@ -67,9 +68,11 @@ import { PlatformStatesClientPK, PlatformStatesClientEntry, makePlatformStatesClientPK, + unsafeBrandId, } from "pagopa-interop-models"; -import { AuthData } from "pagopa-interop-commons"; +import { AuthData, dateToSeconds } from "pagopa-interop-commons"; import { z } from "zod"; +import * as jose from "jose"; import { match } from "ts-pattern"; export function expectPastTimestamp(timestamp: bigint): boolean { @@ -419,7 +422,9 @@ export const getMockDelegationDocument = ( export const getMockTokenStatesClientPurposeEntry = ( tokenStateEntryPK?: TokenGenerationStatesClientKidPurposePK ): TokenGenerationStatesClientPurposeEntry => { - const clientId = generateId(); + const clientId = tokenStateEntryPK + ? unsafeBrandId(tokenStateEntryPK.split("#")[1]) + : generateId(); const purposeId = generateId(); const consumerId = generateId(); const eserviceId = generateId(); @@ -436,7 +441,7 @@ export const getMockTokenStatesClientPurposeEntry = ( kid, purposeId, }), - descriptorState: itemState.inactive, + descriptorState: itemState.active, descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], descriptorVoucherLifespan: 60, updatedAt: new Date().toISOString(), @@ -457,7 +462,7 @@ export const getMockTokenStatesClientPurposeEntry = ( descriptorId, }), GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, + purposeState: itemState.active, GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, purposeId, @@ -486,7 +491,10 @@ export const getMockAgreementEntry = ( export const getMockTokenStatesClientEntry = ( tokenStateEntryPK?: TokenGenerationStatesClientKidPK ): TokenGenerationStatesClientEntry => { - const clientId = generateId(); + const clientId = tokenStateEntryPK + ? unsafeBrandId(tokenStateEntryPK.split("#")[1]) + : generateId(); + const consumerId = generateId(); const kid = `kid ${Math.random()}`; @@ -517,3 +525,103 @@ export const getMockPlatformStatesClientEntry = ( clientConsumerId: generateId(), clientPurposesIds: [], }); + +export const getMockClientAssertion = async (props?: { + standardClaimsOverride?: Partial; + customClaims?: { [k: string]: unknown }; + customHeader?: { [k: string]: unknown }; +}): Promise<{ + jws: string; + clientAssertion: { + payload: jose.JWTPayload; + header: jose.JWTHeaderParameters; + }; + publicKeyEncodedPem: string; +}> => { + const { keySet, publicKeyEncodedPem } = generateKeySet(); + + const threeHourLater = new Date(); + threeHourLater.setHours(threeHourLater.getHours() + 3); + + const clientId = generateId(); + const defaultPayload: jose.JWTPayload = { + iss: clientId, + sub: clientId, + aud: ["test.interop.pagopa.it", "dev.interop.pagopa.it"], + exp: dateToSeconds(threeHourLater), + jti: generateId(), + iat: dateToSeconds(new Date()), + }; + + const actualPayload: jose.JWTPayload = { + ...defaultPayload, + ...props?.standardClaimsOverride, + ...props?.customClaims, + }; + + const headers: jose.JWTHeaderParameters = { + alg: "RS256", + kid: "kid", + ...props?.customHeader, + }; + + const jws = await signClientAssertion({ + payload: actualPayload, + headers, + keySet, + }); + + return { + jws, + clientAssertion: { + payload: actualPayload, + header: headers, + }, + publicKeyEncodedPem, + }; +}; + +export const generateKeySet = (): { + keySet: crypto.KeyPairKeyObjectResult; + publicKeyEncodedPem: string; +} => { + const keySet: crypto.KeyPairKeyObjectResult = crypto.generateKeyPairSync( + "rsa", + { + modulusLength: 2048, + } + ); + + const pemPublicKey = keySet.publicKey + .export({ + type: "spki", + format: "pem", + }) + .toString(); + + const publicKeyEncodedPem = Buffer.from(pemPublicKey).toString("base64"); + return { + keySet, + publicKeyEncodedPem, + }; +}; + +const signClientAssertion = async ({ + payload, + headers, + keySet, +}: { + payload: jose.JWTPayload; + headers: jose.JWTHeaderParameters; + keySet: crypto.KeyPairKeyObjectResult; +}): Promise => { + const pemPrivateKey = keySet.privateKey.export({ + type: "pkcs8", + format: "pem", + }); + + const privateKey = crypto.createPrivateKey(pemPrivateKey); + return await new jose.SignJWT(payload) + .setProtectedHeader(headers) + .sign(privateKey); +}; diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 18f5e7df98..ca231469ad 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -20,10 +20,46 @@ import { PlatformStatesPurposeEntry, PlatformStatesAgreementEntry, TokenGenerationStatesGenericEntry, + TokenGenerationStatesClientEntry, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; +export const writeTokenStateClientEntry = async ( + tokenStateEntry: TokenGenerationStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: tokenStateEntry.PK, + }, + updatedAt: { + S: tokenStateEntry.updatedAt, + }, + consumerId: { + S: tokenStateEntry.consumerId, + }, + clientKind: { + S: tokenStateEntry.clientKind, + }, + publicKey: { + S: tokenStateEntry.publicKey, + }, + GSIPK_clientId: { + S: tokenStateEntry.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenStateEntry.GSIPK_kid, + }, + }, + TableName: "token-generation-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + export const writeTokenStateEntry = async ( tokenStateEntry: TokenGenerationStatesClientPurposeEntry, dynamoDBClient: DynamoDBClient diff --git a/packages/commons/src/config/authorizationServerTokenGenerationConfig.ts b/packages/commons/src/config/authorizationServerTokenGenerationConfig.ts new file mode 100644 index 0000000000..405d6999eb --- /dev/null +++ b/packages/commons/src/config/authorizationServerTokenGenerationConfig.ts @@ -0,0 +1,22 @@ +import { z } from "zod"; + +export const AuthorizationServerTokenGenerationConfig = z + .object({ + GENERATED_INTEROP_TOKEN_KID: z.string(), + GENERATED_INTEROP_TOKEN_ISSUER: z.string(), + GENERATED_INTEROP_TOKEN_M2M_AUDIENCE: z.string(), + GENERATED_INTEROP_TOKEN_M2M_DURATION_SECONDS: z.string(), + }) + .transform((c) => ({ + generatedInteropTokenKid: c.GENERATED_INTEROP_TOKEN_KID, + generatedInteropTokenIssuer: c.GENERATED_INTEROP_TOKEN_ISSUER, + generatedInteropTokenM2MAudience: c.GENERATED_INTEROP_TOKEN_M2M_AUDIENCE, + generatedInteropTokenM2MDurationSeconds: parseInt( + c.GENERATED_INTEROP_TOKEN_M2M_DURATION_SECONDS, + 10 + ), + })); + +export type AuthorizationServerTokenGenerationConfig = z.infer< + typeof AuthorizationServerTokenGenerationConfig +>; diff --git a/packages/commons/src/config/index.ts b/packages/commons/src/config/index.ts index 7cba6068cc..6993d3b5d9 100644 --- a/packages/commons/src/config/index.ts +++ b/packages/commons/src/config/index.ts @@ -15,3 +15,4 @@ export * from "./sessionTokenGenerationConfig.js"; export * from "./redisRateLimiterConfig.js"; export * from "./pecEmailManagerConfig.js"; export * from "./selfcareConfig.js"; +export * from "./authorizationServerTokenGenerationConfig.js"; diff --git a/packages/commons/src/interop-token/interopTokenService.ts b/packages/commons/src/interop-token/interopTokenService.ts index b8088b4767..f4fec60d4f 100644 --- a/packages/commons/src/interop-token/interopTokenService.ts +++ b/packages/commons/src/interop-token/interopTokenService.ts @@ -1,12 +1,28 @@ import crypto from "crypto"; import { KMSClient, SignCommand, SignCommandInput } from "@aws-sdk/client-kms"; +import { + ClientId, + generateId, + PurposeId, + TenantId, + ClientAssertionDigest, +} from "pagopa-interop-models"; import { SessionTokenGenerationConfig } from "../config/sessionTokenGenerationConfig.js"; import { TokenGenerationConfig } from "../config/tokenGenerationConfig.js"; +import { AuthorizationServerTokenGenerationConfig } from "../config/authorizationServerTokenGenerationConfig.js"; +import { dateToSeconds } from "../utils/date.js"; import { CustomClaims, + GENERATED_INTEROP_TOKEN_M2M_ROLE, + InteropApiToken, + InteropConsumerToken, + InteropJwtApiPayload, + InteropJwtConsumerPayload, InteropJwtHeader, InteropJwtPayload, InteropToken, + ORGANIZATION_ID_CLAIM, + ROLE_CLAIM, SessionClaims, SessionJwtPayload, SessionToken, @@ -22,14 +38,26 @@ export class InteropTokenGenerator { private kmsClient: KMSClient; constructor( - private config: TokenGenerationConfig & - Partial + private config: Partial & + Partial & + Partial, + kmsClient?: KMSClient ) { - this.kmsClient = new KMSClient(); + this.kmsClient = kmsClient || new KMSClient(); } public async generateInternalToken(): Promise { - const currentTimestamp = Math.floor(Date.now() / 1000); + const currentTimestamp = dateToSeconds(new Date()); + + if ( + !this.config.kid || + !this.config.issuer || + !this.config.audience || + !this.config.subject || + !this.config.secondsDuration + ) { + throw Error("TokenGenerationConfig not provided or incomplete"); + } const header: InteropJwtHeader = { alg: JWT_HEADER_ALG, @@ -49,11 +77,11 @@ export class InteropTokenGenerator { [JWT_ROLE_CLAIM]: JWT_INTERNAL_ROLE, }; - const serializedToken = await this.createAndSignToken( + const serializedToken = await this.createAndSignToken({ header, payload, - this.config.kid - ); + keyId: this.config.kid, + }); return { header, @@ -75,7 +103,7 @@ export class InteropTokenGenerator { throw Error("SessionTokenGenerationConfig not provided or incomplete"); } - const currentTimestamp = Math.floor(Date.now() / 1000); + const currentTimestamp = dateToSeconds(new Date()); const header: InteropJwtHeader = { alg: JWT_HEADER_ALG, @@ -96,11 +124,122 @@ export class InteropTokenGenerator { ...claims, }; - const serializedToken = await this.createAndSignToken( + const serializedToken = await this.createAndSignToken({ + header, + payload, + keyId: this.config.generatedKid, + }); + + return { + header, + payload, + serialized: serializedToken, + }; + } + + public async generateInteropApiToken({ + sub, + consumerId, + }: { + sub: ClientId; + consumerId: TenantId; + }): Promise { + if ( + !this.config.generatedInteropTokenKid || + !this.config.generatedInteropTokenIssuer || + !this.config.generatedInteropTokenM2MAudience || + !this.config.generatedInteropTokenM2MDurationSeconds + ) { + throw Error( + "AuthorizationServerTokenGenerationConfig not provided or incomplete" + ); + } + + const currentTimestamp = Date.now(); + + const header: InteropJwtHeader = { + alg: "RS256", + use: "sig", + typ: "at+jwt", + kid: this.config.generatedInteropTokenKid, + }; + + const payload: InteropJwtApiPayload = { + jti: generateId(), + iss: this.config.generatedInteropTokenIssuer, + aud: [this.config.generatedInteropTokenM2MAudience], + sub, + iat: currentTimestamp, + nbf: currentTimestamp, + exp: + currentTimestamp + + this.config.generatedInteropTokenM2MDurationSeconds * 1000, + [ORGANIZATION_ID_CLAIM]: consumerId, + [ROLE_CLAIM]: GENERATED_INTEROP_TOKEN_M2M_ROLE, + }; + + const serializedToken = await this.createAndSignToken({ + header, + payload, + keyId: this.config.generatedInteropTokenKid, + }); + + return { + header, + payload, + serialized: serializedToken, + }; + } + + public async generateInteropConsumerToken({ + sub, + audience, + purposeId, + tokenDurationInSeconds, + digest, + }: { + sub: ClientId; + audience: string[]; + purposeId: PurposeId; + tokenDurationInSeconds: number; + digest: ClientAssertionDigest | undefined; + }): Promise { + if ( + !this.config.generatedInteropTokenKid || + !this.config.generatedInteropTokenIssuer || + !this.config.generatedInteropTokenM2MAudience + ) { + throw Error( + "AuthorizationServerTokenGenerationConfig not provided or incomplete" + ); + } + + const currentTimestamp = Date.now(); + + const header: InteropJwtHeader = { + alg: "RS256", + use: "sig", + typ: "at+jwt", + kid: this.config.generatedInteropTokenKid, + }; + + const payload: InteropJwtConsumerPayload = { + jti: generateId(), + iss: this.config.generatedInteropTokenIssuer, + aud: audience, + sub, + iat: currentTimestamp, + nbf: currentTimestamp, + exp: currentTimestamp + tokenDurationInSeconds, + purposeId, + ...(digest ? { digest } : {}), + }; + + const serializedToken = await this.createAndSignToken({ header, payload, - this.config.generatedKid - ); + keyId: this.config.generatedInteropTokenKid, + }); return { header, @@ -109,11 +248,15 @@ export class InteropTokenGenerator { }; } - private async createAndSignToken( - header: InteropJwtHeader, - payload: InteropJwtPayload | SessionJwtPayload, - keyId: string - ): Promise { + private async createAndSignToken({ + header, + payload, + keyId, + }: { + header: InteropJwtHeader; + payload: InteropJwtPayload | SessionJwtPayload | InteropJwtConsumerPayload; + keyId: string; + }): Promise { const serializedToken = `${b64UrlEncode( JSON.stringify(header) )}.${b64UrlEncode(JSON.stringify(payload))}`; diff --git a/packages/commons/src/interop-token/models.ts b/packages/commons/src/interop-token/models.ts index 45c16a577e..9054a5d483 100644 --- a/packages/commons/src/interop-token/models.ts +++ b/packages/commons/src/interop-token/models.ts @@ -1,3 +1,4 @@ +import { ClientAssertionDigest } from "pagopa-interop-models"; import { z } from "zod"; export const ORGANIZATION = "organization"; @@ -11,6 +12,9 @@ export const ORGANIZATION_EXTERNAL_ID_CLAIM = "externalId"; export const ORGANIZATION_EXTERNAL_ID_ORIGIN_CLAIM = "origin"; export const ORGANIZATION_EXTERNAL_ID_VALUE_CLAIM = "value"; export const USER_ROLES = "user-roles"; +const PURPOSE_ID_CLAIM = "purposeId"; +export const GENERATED_INTEROP_TOKEN_M2M_ROLE = "m2m"; +export const ROLE_CLAIM = "role"; export interface InteropJwtHeader { alg: string; @@ -28,6 +32,18 @@ export type InteropJwtCommonPayload = { exp: number; }; +export type InteropJwtConsumerPayload = InteropJwtCommonPayload & { + sub: string; + [PURPOSE_ID_CLAIM]: string; + digest?: ClientAssertionDigest; +}; + +export type InteropJwtApiPayload = InteropJwtCommonPayload & { + sub: string; + [ORGANIZATION_ID_CLAIM]: string; + [ROLE_CLAIM]: string; +}; + export type InteropJwtPayload = InteropJwtCommonPayload & { sub: string; role: string; @@ -39,6 +55,18 @@ export type InteropToken = { serialized: string; }; +export type InteropConsumerToken = { + header: InteropJwtHeader; + payload: InteropJwtConsumerPayload; + serialized: string; +}; + +export type InteropApiToken = { + header: InteropJwtHeader; + payload: InteropJwtApiPayload; + serialized: string; +}; + const Organization = z.object({ id: z.string(), name: z.string(), diff --git a/packages/commons/src/utils/date.ts b/packages/commons/src/utils/date.ts index 7d26258daf..8e4dcace25 100644 --- a/packages/commons/src/utils/date.ts +++ b/packages/commons/src/utils/date.ts @@ -13,6 +13,14 @@ export function formatDateyyyyMMddThhmmss(date: Date): string { return format(date, "yyyy-MM-dd'T'hh:mm:ss"); } +export function formatDateyyyyMMdd(date: Date): string { + return format(date, "yyyyMMdd"); +} + +export function formatTimehhmmss(date: Date): string { + return format(date, "hhmmss"); +} + export function dateAtRomeZone(date: Date): string { return formatInTimeZone(date, "Europe/Rome", "dd/MM/yyyy"); } @@ -20,3 +28,7 @@ export function dateAtRomeZone(date: Date): string { export function timeAtRomeZone(date: Date): string { return formatInTimeZone(date, "Europe/Rome", "HH:mm:ss"); } + +export function dateToSeconds(date: Date): number { + return Math.floor(date.getTime() / 1000); +} diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 3da409a6e9..201c9c74dd 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -1,4 +1,4 @@ -import { randomUUID } from "crypto"; +import crypto from "crypto"; import { z } from "zod"; export const CorrelationId = z.string().brand("CorrelationId"); @@ -177,7 +177,7 @@ type IDS = // it infers the type of the ID based on how is used the result // the 'as' is used to cast the uuid string to the inferred type export function generateId(): T { - return randomUUID() as T; + return crypto.randomUUID() as T; } // This function is used to get a branded ID from a string diff --git a/packages/models/src/client-assertion/clientAssertionValidation.ts b/packages/models/src/client-assertion/clientAssertionValidation.ts new file mode 100644 index 0000000000..1a12ce3ca8 --- /dev/null +++ b/packages/models/src/client-assertion/clientAssertionValidation.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; +import { ClientId, PurposeId } from "../brandedIds.js"; + +export const ClientAssertionDigest = z + .object({ + alg: z.string(), + value: z.string(), + }) + .strict(); +export type ClientAssertionDigest = z.infer; + +export const ClientAssertionHeader = z + .object({ + kid: z.string(), + alg: z.string(), + typ: z.string().optional(), + }) + .strict(); +export type ClientAssertionHeader = z.infer; + +export const ClientAssertionPayload = z + .object({ + sub: ClientId, + jti: z.string(), + iat: z.number(), + iss: z.string(), + aud: z.array(z.string()).or(z.string()), + exp: z.number(), + digest: ClientAssertionDigest.optional(), + purposeId: PurposeId.optional(), + }) + .strict(); +export type ClientAssertionPayload = z.infer; + +export const ClientAssertion = z + .object({ + header: ClientAssertionHeader, + payload: ClientAssertionPayload, + }) + .strict(); +export type ClientAssertion = z.infer; diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index 8e7aeee6e8..efba64d4d7 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -62,6 +62,8 @@ export * from "./user/user.js"; export * from "./token-generation-readmodel/platform-states-entry.js"; export * from "./token-generation-readmodel/token-generation-states-entry.js"; export * from "./token-generation-readmodel/commons.js"; +export * from "./token-generation-audit/audit.js"; +export * from "./client-assertion/clientAssertionValidation.js"; // Protobuf export * from "./protobuf/protobuf.js"; diff --git a/packages/models/src/token-generation-audit/audit.ts b/packages/models/src/token-generation-audit/audit.ts new file mode 100644 index 0000000000..b3376dc26d --- /dev/null +++ b/packages/models/src/token-generation-audit/audit.ts @@ -0,0 +1,48 @@ +import { z } from "zod"; +import { + AgreementId, + ClientId, + DescriptorId, + EServiceId, + PurposeId, + PurposeVersionId, + TenantId, +} from "../brandedIds.js"; + +export const ClientAssertionAuditDetails = z.object({ + jwtId: z.string(), + issuedAt: z.number(), + algorithm: z.string(), + keyId: z.string(), + issuer: z.string(), + subject: ClientId, + audience: z.string(), + expirationTime: z.number(), +}); +export type ClientAssertionAuditDetails = z.infer< + typeof ClientAssertionAuditDetails +>; + +export const GeneratedTokenAuditDetails = z.object({ + jwtId: z.string(), + correlationId: z.string(), + issuedAt: z.number(), + clientId: ClientId, + organizationId: TenantId, + agreementId: AgreementId, + eserviceId: EServiceId, + descriptorId: DescriptorId, + purposeId: PurposeId, + purposeVersionId: PurposeVersionId, + algorithm: z.string(), + keyId: z.string(), + audience: z.string(), + subject: z.string(), + notBefore: z.number(), + expirationTime: z.number(), + issuer: z.string(), + clientAssertion: ClientAssertionAuditDetails, +}); +export type GeneratedTokenAuditDetails = z.infer< + typeof GeneratedTokenAuditDetails +>; diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index c36a56cf9a..1f82f59bf6 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -46,6 +46,12 @@ export type TokenGenerationStatesClientPurposeEntry = z.infer< typeof TokenGenerationStatesClientPurposeEntry >; +export const FullTokenGenerationStatesClientPurposeEntry = + TokenGenerationStatesClientPurposeEntry.required(); +export type FullTokenGenerationStatesClientPurposeEntry = z.infer< + typeof FullTokenGenerationStatesClientPurposeEntry +>; + export const TokenGenerationStatesClientEntry = TokenGenerationStatesBaseEntry.extend({ PK: TokenGenerationStatesClientKidPK, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1885d59312..81acb9ffd1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -808,6 +808,97 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/authorization-server: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.637.0 + version: 3.637.0 + '@aws-sdk/client-kms': + specifier: 3.600.0 + version: 3.600.0 + '@aws-sdk/util-dynamodb': + specifier: 3.637.0 + version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) + axios: + specifier: 1.7.4 + version: 1.7.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + express: + specifier: 4.20.0 + version: 4.20.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 + pagopa-interop-api-clients: + specifier: workspace:* + version: link:../api-clients + pagopa-interop-client-assertion-validation: + specifier: workspace:* + version: link:../client-assertion-validation + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + '@types/uuid': + specifier: 9.0.8 + version: 9.0.8 + jose: + specifier: 5.9.4 + version: 5.9.4 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + uuid: + specifier: 10.0.0 + version: 10.0.0 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/authorization-updater: dependencies: '@protobuf-ts/runtime': @@ -1482,6 +1573,9 @@ importers: dotenv-flow: specifier: 4.1.0 version: 4.1.0 + jose: + specifier: 5.9.4 + version: 5.9.4 jsonwebtoken: specifier: 9.0.2 version: 9.0.2 @@ -4949,6 +5043,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -7699,6 +7796,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -8093,8 +8194,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-endpoint-discovery': 3.598.0 @@ -8240,8 +8341,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8347,8 +8448,8 @@ snapshots: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-bucket-endpoint': 3.598.0 @@ -8455,8 +8556,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8499,11 +8600,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': + '@aws-sdk/client-sso-oidc@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8542,7 +8643,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': @@ -9020,11 +9120,11 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.600.0': + '@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -9063,6 +9163,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' - aws-crt '@aws-sdk/client-sts@3.609.0': @@ -9393,7 +9494,7 @@ snapshots: '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 '@aws-sdk/credential-provider-process': 3.598.0 @@ -9710,7 +9811,7 @@ snapshots: '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.7 '@smithy/types': 3.5.0 @@ -10138,7 +10239,7 @@ snapshots: '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0 '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -11993,6 +12094,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/uuid@9.0.8': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -15196,6 +15299,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@9.0.1: {} vary@1.1.2: {} From ec0ad3e876ce7c5ce1c51acee4dbf6700179fdec Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 19 Nov 2024 11:31:14 +0100 Subject: [PATCH 031/126] IMN-798 Fix voucher lifespan in token-generation readmodel (#1177) Co-authored-by: shuyec <76391491+shuyec@users.noreply.github.com> --- .../consumerServiceV1.integration.test.ts | 2 +- .../src/consumerServiceV2.ts | 41 +- .../catalog-platformstate-writer/src/utils.ts | 125 ++++++ .../test/consumerServiceV1.test.ts | 14 +- .../test/consumerServiceV2.test.ts | 355 +++++++++++++++++- .../test/utils.ts | 10 +- 6 files changed, 516 insertions(+), 31 deletions(-) diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 339672bc55..67c1696479 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -158,7 +158,7 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should update the entry if the incoming version is more recent than existing table entry", async () => { + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index ff27b605a9..7043f7889e 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -20,6 +20,8 @@ import { readCatalogEntry, updateDescriptorStateInPlatformStatesEntry, updateDescriptorStateInTokenGenerationStatesTable, + updateDescriptorVoucherLifespanInPlatformStateEntry, + updateDescriptorVoucherLifespanInTokenGenerationStatesTable, writeCatalogEntry, } from "./utils.js"; @@ -178,6 +180,44 @@ export async function handleMessageV2( dynamoDBClient ); }) + .with({ type: "EServiceDescriptorQuotasUpdated" }, async (msg) => { + const { eservice, descriptor } = parseEServiceAndDescriptor( + msg.data.eservice, + unsafeBrandId(msg.data.descriptorId), + message.type + ); + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const catalogEntry = await readCatalogEntry(primaryKey, dynamoDBClient); + + if (!catalogEntry || catalogEntry.version > msg.version) { + return Promise.resolve(); + } else { + if ( + descriptor.voucherLifespan !== catalogEntry.descriptorVoucherLifespan + ) { + await updateDescriptorVoucherLifespanInPlatformStateEntry( + dynamoDBClient, + primaryKey, + descriptor.voucherLifespan, + msg.version + ); + + // token-generation-states + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + await updateDescriptorVoucherLifespanInTokenGenerationStatesTable( + eserviceId_descriptorId, + descriptor.voucherLifespan, + dynamoDBClient + ); + } + } + }) .with( { type: "EServiceDeleted" }, { type: "EServiceAdded" }, @@ -186,7 +226,6 @@ export async function handleMessageV2( { type: "EServiceDescriptorAdded" }, { type: "EServiceDraftDescriptorDeleted" }, { type: "EServiceDraftDescriptorUpdated" }, - { type: "EServiceDescriptorQuotasUpdated" }, { type: "EServiceDescriptorInterfaceAdded" }, { type: "EServiceDescriptorDocumentAdded" }, { type: "EServiceDescriptorInterfaceUpdated" }, diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index e01e9d572c..924d39fd68 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -148,6 +148,39 @@ export const updateDescriptorStateInPlatformStatesEntry = async ( await dynamoDBClient.send(command); }; +export const updateDescriptorVoucherLifespanInPlatformStateEntry = async ( + dynamoDBClient: DynamoDBClient, + primaryKey: PlatformStatesEServiceDescriptorPK, + voucherLifespan: number, + version: number +): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":newVoucherLifespan": { + N: voucherLifespan.toString(), + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET descriptorVoucherLifespan = :newVoucherLifespan, version = :newVersion, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); +}; + export const updateDescriptorStateInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, descriptorState: ItemState, @@ -217,6 +250,67 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( ); }; +export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = + async ( + eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + voucherLifespan: number, + dynamoDBClient: DynamoDBClient + ): Promise => { + const runPaginatedQuery = async ( + eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Descriptor", + KeyConditionExpression: `GSIPK_eserviceId_descriptorId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: eserviceId_descriptorId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateDescriptorVoucherLifespanInTokenGenerationStatesEntries( + voucherLifespan, + dynamoDBClient, + tokenStateEntries.data + ); + + if (data.LastEvaluatedKey) { + await runPaginatedQuery( + eserviceId_descriptorId, + dynamoDBClient, + data.LastEvaluatedKey + ); + } + } + }; + + await runPaginatedQuery(eserviceId_descriptorId, dynamoDBClient, undefined); + }; + const updateDescriptorStateEntriesInTokenGenerationStatesTable = async ( descriptorState: ItemState, dynamoDBClient: DynamoDBClient, @@ -247,3 +341,34 @@ const updateDescriptorStateEntriesInTokenGenerationStatesTable = async ( await dynamoDBClient.send(command); } }; + +const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( + voucherLifespan: number, + dynamoDBClient: DynamoDBClient, + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[] +): Promise => { + for (const entry of entriesToUpdate) { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":newVoucherLifespan": { + N: voucherLifespan.toString(), + }, + ":newUpdateAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET descriptorVoucherLifespan = :newVoucherLifespan, updatedAt = :newUpdateAt", + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts index 9ba36ba2d5..74cc5351c4 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts @@ -38,7 +38,7 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { writeTokenStateEntry } from "pagopa-interop-commons-test"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; -import { config, sleep } from "./utils.js"; +import { config } from "./utils.js"; describe("V1 events", async () => { if (!config) { fail(); @@ -124,7 +124,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const primaryKey = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, @@ -169,7 +168,7 @@ describe("V1 events", async () => { ]) ); }); - it("(suspended -> published) should update the entry if incoming version is more recent than existing table entry", async () => { + it("(suspended -> published) should update the entry if the incoming version is more recent than the existing table entry", async () => { const publishedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -244,7 +243,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedCatalogEntry = await readCatalogEntry( primaryKey, @@ -358,7 +356,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedCatalogEntry = await readCatalogEntry( catalogPrimaryKey, @@ -381,7 +378,7 @@ describe("V1 events", async () => { }); describe("(published -> suspended)", () => { - it("should update the entry if msg.version >= existing version", async () => { + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -458,7 +455,6 @@ describe("V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -497,7 +493,7 @@ describe("V1 events", async () => { ); }); - it("should do no operation if msg.version < existing version", async () => { + it("should do no operation if the existing table entry is more recent", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -574,7 +570,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -716,7 +711,6 @@ describe("V1 events", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); - await sleep(1000, mockDate); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); expect(retrievedEntry).toBeUndefined(); diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts index 104c5cf158..5f94de02ee 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts @@ -16,6 +16,7 @@ import { EServiceDescriptorActivatedV2, EServiceDescriptorArchivedV2, EServiceDescriptorPublishedV2, + EServiceDescriptorQuotasUpdatedV2, EServiceDescriptorSuspendedV2, EServiceEventEnvelope, PlatformStatesCatalogEntry, @@ -41,7 +42,7 @@ import { } from "pagopa-interop-commons-test"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; -import { config, sleep } from "./utils.js"; +import { config } from "./utils.js"; describe("integration tests V2 events", async () => { if (!config) { @@ -138,7 +139,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -237,7 +237,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -346,7 +345,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); expect( handleMessageV2(message, dynamoDBClient) ).resolves.not.toThrowError(); @@ -448,7 +446,6 @@ describe("integration tests V2 events", async () => { GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); @@ -755,7 +752,6 @@ describe("integration tests V2 events", async () => { GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); @@ -898,7 +894,7 @@ describe("integration tests V2 events", async () => { }); describe("EServiceDescriptorSuspended", () => { - it("should do no operation if the entry already exists: incoming has version 1; previous entry has version 2", async () => { + it("should do no operation if the existing table entry is more recent", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -993,7 +989,7 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry: incoming has version 3; previous entry has version 2", async () => { + it("should update the entry if the incoming version is more recent than existing table entry", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -1105,7 +1101,7 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should not throw error if entry doesn't exist", async () => { + it("should do no operation if entry doesn't exist", async () => { const suspendedDescriptor: Descriptor = { ...getMockDescriptor(), audience: ["pagopa.it/test1", "pagopa.it/test2"], @@ -1170,7 +1166,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); expect( handleMessageV2(message, dynamoDBClient) ).resolves.not.toThrowError(); @@ -1198,4 +1193,344 @@ describe("integration tests V2 events", async () => { ); }); }); + + describe("EServiceDescriptorQuotasUpdated", () => { + it("should do no operation if the existing version is more recent", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + audience: ["pagopa.it/test1", "pagopa.it/test2"], + interface: getMockDocument(), + state: descriptorState.suspended, + publishedAt: new Date(), + suspendedAt: new Date(), + voucherLifespan: 60, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + const updatedDescriptor: Descriptor = { + ...descriptor, + voucherLifespan: 120, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [updatedDescriptor], + }; + + const payload: EServiceDescriptorQuotasUpdatedV2 = { + eservice: toEServiceV2(updatedEService), + descriptorId: updatedDescriptor.id, + }; + const message: EServiceEventEnvelope = { + sequence_num: 1, + stream_id: eservice.id, + version: 1, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: updatedDescriptor.id, + }); + const previousStateEntry: PlatformStatesCatalogEntry = { + PK: primaryKey, + state: itemState.active, + descriptorAudience: updatedDescriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); + + expect(retrievedEntry).toEqual(previousStateEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByEserviceIdAndDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry2, + previousTokenStateEntry1, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + audience: ["pagopa.it/test1", "pagopa.it/test2"], + interface: getMockDocument(), + state: descriptorState.suspended, + publishedAt: new Date(), + suspendedAt: new Date(), + voucherLifespan: 60, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + const updatedDescriptor: Descriptor = { + ...descriptor, + voucherLifespan: 120, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [updatedDescriptor], + }; + + const payload: EServiceDescriptorQuotasUpdatedV2 = { + eservice: toEServiceV2(updatedEService), + descriptorId: updatedDescriptor.id, + }; + const message: EServiceEventEnvelope = { + sequence_num: 1, + stream_id: eservice.id, + version: 3, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: updatedDescriptor.id, + }); + const previousStateEntry: PlatformStatesCatalogEntry = { + PK: primaryKey, + state: itemState.active, + descriptorAudience: updatedDescriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(previousStateEntry, dynamoDBClient); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + await handleMessageV2(message, dynamoDBClient); + + const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); + const expectedEntry: PlatformStatesCatalogEntry = { + ...previousStateEntry, + version: 3, + descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + expect(retrievedEntry).toEqual(expectedEntry); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByEserviceIdAndDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + expectedTokenStateEntry2, + expectedTokenStateEntry1, + ]) + ); + }); + it("should do no operation if entry doesn't exist", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + audience: ["pagopa.it/test1", "pagopa.it/test2"], + interface: getMockDocument(), + state: descriptorState.suspended, + publishedAt: new Date(), + suspendedAt: new Date(), + voucherLifespan: 60, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + const updatedDescriptor: Descriptor = { + ...descriptor, + voucherLifespan: 120, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [updatedDescriptor], + }; + + const payload: EServiceDescriptorQuotasUpdatedV2 = { + eservice: toEServiceV2(updatedEService), + descriptorId: updatedDescriptor.id, + }; + const message: EServiceEventEnvelope = { + sequence_num: 1, + stream_id: eservice.id, + version: 3, + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const primaryKey = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: updatedDescriptor.id, + }); + + // token-generation-states + const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + + const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + + expect( + handleMessageV2(message, dynamoDBClient) + ).resolves.not.toThrowError(); + + // platform-states + const retrievedCatalogEntry = await readCatalogEntry( + primaryKey, + dynamoDBClient + ); + expect(retrievedCatalogEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenStateEntries = + await readTokenStateEntriesByEserviceIdAndDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); + + expect(retrievedTokenStateEntries).toHaveLength(2); + expect(retrievedTokenStateEntries).toEqual( + expect.arrayContaining([ + previousTokenStateEntry1, + previousTokenStateEntry2, + ]) + ); + }); + }); }); diff --git a/packages/catalog-platformstate-writer/test/utils.ts b/packages/catalog-platformstate-writer/test/utils.ts index fdc2e3c2c5..aca07c9cc6 100644 --- a/packages/catalog-platformstate-writer/test/utils.ts +++ b/packages/catalog-platformstate-writer/test/utils.ts @@ -1,11 +1,3 @@ -import { inject, vi } from "vitest"; +import { inject } from "vitest"; export const config = inject("tokenGenerationReadModelConfig"); - -export const sleep = (ms: number, mockDate = new Date()): Promise => - new Promise((resolve) => { - vi.useRealTimers(); - setTimeout(resolve, ms); - vi.useFakeTimers(); - vi.setSystemTime(mockDate); - }); From d2ed805c0bc1a6985e06d2577bd4faebd79751a5 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 20 Nov 2024 10:38:07 +0100 Subject: [PATCH 032/126] Delegation process improvements in errors, tests, API spec (#1209) --- collections/collection.bru | 1 + ...r delegations.bru => List delegations.bru} | 6 +- .../producer => }/Retrieves a delegation.bru | 2 +- .../health/Health status endpoint.bru | 0 .../producer/Approves a delegation.bru | 6 +- .../producer/Delegation Creation.bru | 10 +- .../producer/Rejects a delegation.bru | 8 +- .../producer/Revoke a delegation.bru | 6 +- collections/environments/PagoPA local.bru | 1 + .../api-clients/open-api/delegationApi.yml | 83 +++--- packages/commons-test/src/testUtils.ts | 10 +- .../src/model/domain/errors.ts | 25 +- .../src/services/delegationProducerService.ts | 17 +- .../src/services/validators.ts | 58 ++-- .../src/utilities/errorMappers.ts | 10 +- .../test/approveProducerDelegation.test.ts | 17 +- .../test/createProducerDelegation.test.ts | 260 +++++++++++------- .../test/getDelegationById.test.ts | 16 +- .../test/getDelegations.test.ts | 12 +- .../test/rejectProducerDelegation.test.ts | 14 +- .../test/revokeProducerDelegation.test.ts | 20 +- .../test/consumerServiceV2.test.ts | 10 +- 22 files changed, 365 insertions(+), 227 deletions(-) rename collections/delegation/{Delegation Process Micro Service/producer/List producer delegations.bru => List delegations.bru} (79%) rename collections/delegation/{Delegation Process Micro Service/producer => }/Retrieves a delegation.bru (82%) rename collections/delegation/{Delegation Process Micro Service => }/health/Health status endpoint.bru (100%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Approves a delegation.bru (76%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Delegation Creation.bru (64%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Rejects a delegation.bru (68%) rename collections/delegation/{Delegation Process Micro Service => }/producer/Revoke a delegation.bru (79%) diff --git a/collections/collection.bru b/collections/collection.bru index 2597da5bd6..5c993b3ebd 100644 --- a/collections/collection.bru +++ b/collections/collection.bru @@ -1,5 +1,6 @@ vars:pre-request { tenantId: 69e2865e-65ab-4e48-a638-2037a9ee2ee7 + tenantId2: 0cf1db41-3085-43a6-9e4c-57e0fb81a916 userId1: f07ddb8f-17f9-47d4-b31e-35d1ac10e521 userId2: 2a1614d7-c1aa-4148-895f-dcadb75b6660 keyEncodedPem: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FETUVrdEZVVyszY2VWN01FT2RBVG5ZYWc3eQpJc3JtRDZ6eVBTZHhJTDlJczNmdnlGREMvbnVCWVFpa3Izampjc21aREdTN0RGKzNWRUJ3UXFYUldGM3NObElRCnFIc21Td2x2NjZ2ZDQ0OHEzSXpSb1JBWktGMGc3c3BGcUJ5bi9DTXZaM0RET2xVK2V0c2xDYWRNa084UktyM1YKd2xqQjFJdk90TWtCd2lLTU53SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo= diff --git a/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru b/collections/delegation/List delegations.bru similarity index 79% rename from collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru rename to collections/delegation/List delegations.bru index 733ec7c274..a7503108d1 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/List producer delegations.bru +++ b/collections/delegation/List delegations.bru @@ -1,5 +1,5 @@ meta { - name: List producer delegations + name: List delegations type: http seq: 1 } @@ -24,3 +24,7 @@ headers { Authorization: {{JWT}} X-Correlation-Id: {{correlation-id}} } + +vars:post-response { + delegationId: res.body.results.at(-1).id +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru b/collections/delegation/Retrieves a delegation.bru similarity index 82% rename from collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru rename to collections/delegation/Retrieves a delegation.bru index 4e9425bd6d..398d20875c 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Retrieves a delegation.bru +++ b/collections/delegation/Retrieves a delegation.bru @@ -11,7 +11,7 @@ get { } params:path { - delegationId: 123e4567-e89b-12d3-a456-426614174000 + delegationId: {{delegationId}} } headers { diff --git a/collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru b/collections/delegation/health/Health status endpoint.bru similarity index 100% rename from collections/delegation/Delegation Process Micro Service/health/Health status endpoint.bru rename to collections/delegation/health/Health status endpoint.bru diff --git a/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru b/collections/delegation/producer/Approves a delegation.bru similarity index 76% rename from collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru rename to collections/delegation/producer/Approves a delegation.bru index 23bd4e2e56..3f94f781fd 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Approves a delegation.bru +++ b/collections/delegation/producer/Approves a delegation.bru @@ -1,7 +1,7 @@ meta { name: Approves a delegation type: http - seq: 4 + seq: 2 } post { @@ -11,10 +11,10 @@ post { } params:path { - delegationId: + delegationId: {{delegationId}} } headers { - Authorization: {{JWT}} + Authorization: {{JWT2}} X-Correlation-Id: {{correlation-id}} } diff --git a/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru b/collections/delegation/producer/Delegation Creation.bru similarity index 64% rename from collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru rename to collections/delegation/producer/Delegation Creation.bru index fa36a2e7e5..4e1656fadf 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Delegation Creation.bru +++ b/collections/delegation/producer/Delegation Creation.bru @@ -1,7 +1,7 @@ meta { name: Delegation Creation type: http - seq: 3 + seq: 1 } post { @@ -17,7 +17,11 @@ headers { body:json { { - "eserviceId": "", - "delegateId": "" + "eserviceId": "{{eserviceId}}", + "delegateId": "{{tenantId2}}" } } + +vars:post-response { + delegationId: res.body.id +} diff --git a/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru b/collections/delegation/producer/Rejects a delegation.bru similarity index 68% rename from collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru rename to collections/delegation/producer/Rejects a delegation.bru index b2b1477d62..57f0709361 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Rejects a delegation.bru +++ b/collections/delegation/producer/Rejects a delegation.bru @@ -1,7 +1,7 @@ meta { name: Rejects a delegation type: http - seq: 5 + seq: 3 } post { @@ -11,16 +11,16 @@ post { } params:path { - delegationId: + delegationId: {{delegationId}} } headers { - Authorization: {{JWT}} + Authorization: {{JWT2}} X-Correlation-Id: {{correlation-id}} } body:json { { - "rejectionReason": "" + "rejectionReason": "test rejection reason" } } diff --git a/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru b/collections/delegation/producer/Revoke a delegation.bru similarity index 79% rename from collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru rename to collections/delegation/producer/Revoke a delegation.bru index 69741abb1d..392a524b0d 100644 --- a/collections/delegation/Delegation Process Micro Service/producer/Revoke a delegation.bru +++ b/collections/delegation/producer/Revoke a delegation.bru @@ -1,7 +1,7 @@ meta { name: Revoke a delegation type: http - seq: 6 + seq: 4 } delete { @@ -10,6 +10,10 @@ delete { auth: none } +params:path { + delegationId: {{delegationId}} +} + headers { Authorization: {{JWT}} X-Correlation-Id: {{correlation-id}} diff --git a/collections/environments/PagoPA local.bru b/collections/environments/PagoPA local.bru index f0051f88bb..30c756afeb 100644 --- a/collections/environments/PagoPA local.bru +++ b/collections/environments/PagoPA local.bru @@ -12,4 +12,5 @@ vars { host-delegation: http://localhost:3800 host-api-gw: http://localhost:3700/api-gateway/0.0 JWT-M2M: Bearer {{process.env.JWT-M2M}} + JWT2: Bearer {{process.env.JWT2}} } diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml index c87eeff3b2..d912ab74c0 100644 --- a/packages/api-clients/open-api/delegationApi.yml +++ b/packages/api-clients/open-api/delegationApi.yml @@ -33,6 +33,8 @@ tags: url: http://swagger.io paths: /delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" get: description: List delegations summary: List delegations @@ -118,19 +120,21 @@ paths: schema: $ref: "#/components/schemas/Problem" /delegations/{delegationId}: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid get: description: Retrieves a delegation summary: Retrieves a delegation tags: - delegation operationId: getDelegation - parameters: - - name: delegationId - in: path - description: The delegation id - required: true - schema: - type: string responses: "200": description: Delegation retrieved @@ -157,9 +161,11 @@ paths: schema: $ref: "#/components/schemas/Problem" /producer/delegations: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" post: - description: creates the delegation - summary: Delegation Creation + description: Creates a producer delegation + summary: Producer delegation creation tags: - producer operationId: createProducerDelegation @@ -168,7 +174,7 @@ paths: application/json: schema: $ref: "#/components/schemas/DelegationSeed" - description: payload for delegation creation + description: Payload for delegation creation required: true responses: "200": @@ -196,20 +202,21 @@ paths: schema: $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}/approve: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid post: - description: Approves a delegation - summary: Approves a delegation + description: Approves a producer delegation + summary: Producer delegation approval tags: - producer operationId: approveProducerDelegation - parameters: - - name: delegationId - in: path - description: The delegation id - required: true - schema: - type: string - format: uuid responses: "204": description: Delegation approved @@ -238,9 +245,18 @@ paths: schema: $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}/reject: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + format: uuid post: - description: Rejects a delegation - summary: Rejects a delegation + description: Rejects a producer delegation + summary: Producer delegation rejection tags: - producer operationId: rejectProducerDelegation @@ -250,15 +266,7 @@ paths: schema: $ref: "#/components/schemas/RejectDelegationPayload" required: true - description: payload for delegation rejection - parameters: - - name: delegationId - in: path - description: The delegation id - required: true - schema: - type: string - format: uuid + description: Payload for delegation rejection responses: "204": description: Delegation rejected @@ -288,15 +296,17 @@ paths: $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}: parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" - name: delegationId in: path description: The delegation id required: true schema: type: string + format: uuid delete: - description: Revokes a delegation - summary: Revokes a delegation + description: Revokes a producer delegation + summary: Producer delegation revocation tags: - producer operationId: revokeProducerDelegation @@ -337,6 +347,13 @@ paths: schema: $ref: "#/components/schemas/Problem" components: + parameters: + CorrelationIdHeader: + in: header + name: X-Correlation-Id + required: true + schema: + type: string schemas: Delegations: type: object diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 2d8a8d7692..72fbb57eff 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -50,7 +50,6 @@ import { PurposeVersionId, ProducerKeychain, Delegation, - delegationKind, DelegationId, DelegationContractDocument, DelegationContractId, @@ -68,6 +67,7 @@ import { PlatformStatesClientPK, PlatformStatesClientEntry, makePlatformStatesClientPK, + DelegationKind, unsafeBrandId, } from "pagopa-interop-models"; import { AuthData, dateToSeconds } from "pagopa-interop-commons"; @@ -369,7 +369,8 @@ export const getMockAuthData = (organizationId?: TenantId): AuthData => ({ selfcareId: generateId(), }); -export const getMockDelegationProducer = ({ +export const getMockDelegation = ({ + kind, id = generateId(), delegatorId = generateId(), delegateId = generateId(), @@ -378,6 +379,7 @@ export const getMockDelegationProducer = ({ activationContract = undefined, revocationContract = undefined, }: { + kind: DelegationKind; id?: DelegationId; delegatorId?: TenantId; delegateId?: TenantId; @@ -385,7 +387,7 @@ export const getMockDelegationProducer = ({ state?: DelegationState; activationContract?: DelegationContractDocument; revocationContract?: DelegationContractDocument; -} = {}): Delegation => { +}): Delegation => { const creationTime = new Date(); return { @@ -398,7 +400,7 @@ export const getMockDelegationProducer = ({ state, activationContract, revocationContract, - kind: delegationKind.delegatedProducer, + kind, stamps: { submission: { who: delegatorId, diff --git a/packages/delegation-process/src/model/domain/errors.ts b/packages/delegation-process/src/model/domain/errors.ts index b5b0064bb6..7822e1e03c 100644 --- a/packages/delegation-process/src/model/domain/errors.ts +++ b/packages/delegation-process/src/model/domain/errors.ts @@ -5,7 +5,10 @@ import { makeApiProblemBuilder, TenantId, DelegationState, + DelegationKind, + Tenant, } from "pagopa-interop-models"; +import { match } from "ts-pattern"; export const errorCodes = { delegationNotFound: "0001", @@ -13,7 +16,7 @@ export const errorCodes = { delegationAlreadyExists: "0003", tenantNotFound: "0004", invalidDelegatorAndDelegateIds: "0005", - invalidExternalOriginId: "0006", + tenantIsNotIPAError: "0006", tenantNotAllowedToDelegation: "0007", delegationNotRevokable: "0008", operationNotAllowOnDelegation: "0009", @@ -70,21 +73,27 @@ export function delegatorAndDelegateSameIdError(): ApiError { }); } -export function invalidExternalOriginError( - externalOrigin?: string +export function tenantIsNotIPAError( + tenant: Tenant, + delegatorOrDelegate: "Delegator" | "Delegate" ): ApiError { + const delegatorOrDelegateString = match(delegatorOrDelegate) + .with("Delegator", () => "Delegator") + .with("Delegate", () => "Delegate") + .exhaustive(); return new ApiError({ - detail: `Delegator is not an IPA`, - code: "invalidExternalOriginId", - title: `Invalid External origin ${externalOrigin}`, + detail: `${delegatorOrDelegateString} ${tenant.id} with external origin ${tenant.externalId.origin} is not an IPA`, + code: "tenantIsNotIPAError", + title: `Invalid external origin`, }); } export function tenantNotAllowedToDelegation( - tenantId: string + tenantId: string, + kind: DelegationKind ): ApiError { return new ApiError({ - detail: `Tenant ${tenantId} not allowed to delegation`, + detail: `Tenant ${tenantId} not allowed to receive delegations of kind: ${kind}`, code: "tenantNotAllowedToDelegation", title: "Tenant not allowed to delegation", }); diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index f323a8de45..81c844f3f3 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -32,12 +32,12 @@ import { ReadModelService } from "./readModelService.js"; import { assertDelegationIsRevokable, assertDelegationNotExists, - assertDelegatorIsIPA, assertDelegatorIsNotDelegate, - assertEserviceExists, - assertTenantAllowedToReceiveProducerDelegation, + assertDelegatorIsProducer, + assertTenantAllowedToReceiveDelegation, assertIsDelegate, assertIsState, + assertDelegatorAndDelegateIPA, } from "./validators.js"; import { contractBuilder } from "./delegationContractBuilder.js"; import { retrieveDelegationById } from "./delegationService.js"; @@ -84,9 +84,14 @@ export function delegationProducerServiceBuilder( const delegator = await retrieveTenantById(delegatorId); const delegate = await retrieveTenantById(delegateId); - assertTenantAllowedToReceiveProducerDelegation(delegate); - await assertDelegatorIsIPA(delegator); - await assertEserviceExists(delegatorId, eserviceId, readModelService); + assertTenantAllowedToReceiveDelegation( + delegate, + delegationKind.delegatedProducer + ); + await assertDelegatorAndDelegateIPA(delegator, delegate); + + const eservice = await retrieveEserviceById(eserviceId); + assertDelegatorIsProducer(delegatorId, eservice); await assertDelegationNotExists( delegator, eserviceId, diff --git a/packages/delegation-process/src/services/validators.ts b/packages/delegation-process/src/services/validators.ts index c5a6437d4a..e4060bff3f 100644 --- a/packages/delegation-process/src/services/validators.ts +++ b/packages/delegation-process/src/services/validators.ts @@ -1,30 +1,31 @@ import { Delegation, + delegationKind, DelegationKind, DelegationState, delegationState, + EService, EServiceId, PUBLIC_ADMINISTRATIONS_IDENTIFIER, Tenant, TenantId, } from "pagopa-interop-models"; +import { match } from "ts-pattern"; import { delegationAlreadyExists, delegationNotRevokable, delegatorAndDelegateSameIdError, delegatorNotAllowToRevoke, differentEServiceProducer, - eserviceNotFound, incorrectState, - invalidExternalOriginError, operationRestrictedToDelegate, + tenantIsNotIPAError, tenantNotAllowedToDelegation, - tenantNotFound, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; /* ========= STATES ========= */ -export const delegationNotActivableStates: DelegationState[] = [ +export const inactiveDelegationStates: DelegationState[] = [ delegationState.rejected, delegationState.revoked, ]; @@ -34,17 +35,11 @@ export const activeDelegationStates: DelegationState[] = [ delegationState.active, ]; -export const assertEserviceExists = async ( +export const assertDelegatorIsProducer = ( delegatorId: TenantId, - eserviceId: EServiceId, - readModelService: ReadModelService -): Promise => { - const eservice = await readModelService.getEServiceById(eserviceId); - if (!eservice) { - throw eserviceNotFound(eserviceId); - } - - if (eservice.data.producerId !== delegatorId) { + eservice: EService +): void => { + if (eservice.producerId !== delegatorId) { throw differentEServiceProducer(delegatorId); } }; @@ -58,33 +53,34 @@ export const assertDelegatorIsNotDelegate = ( } }; -export const assertDelegatorIsIPA = async ( - delegator?: Tenant +export const assertDelegatorAndDelegateIPA = async ( + delegator: Tenant, + delegate: Tenant ): Promise => { if (delegator?.externalId?.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { - throw invalidExternalOriginError(delegator?.externalId?.origin); + throw tenantIsNotIPAError(delegator, "Delegator"); + } + + if (delegate?.externalId?.origin !== PUBLIC_ADMINISTRATIONS_IDENTIFIER) { + throw tenantIsNotIPAError(delegate, "Delegate"); } }; -export const assertTenantAllowedToReceiveProducerDelegation = ( - tenant: Tenant +export const assertTenantAllowedToReceiveDelegation = ( + tenant: Tenant, + kind: DelegationKind ): void => { const delegationFeature = tenant.features.find( - (f) => f.type === "DelegatedProducer" + (f) => + f.type === + match(kind) + .with(delegationKind.delegatedProducer, () => "DelegatedProducer") + .with(delegationKind.delegatedConsumer, () => "DelegatedConsumer") + .exhaustive() ); if (!delegationFeature) { - throw tenantNotAllowedToDelegation(tenant.id); - } -}; - -export const assertTenantExists = async ( - tenantId: TenantId, - readModelService: ReadModelService -): Promise => { - const tenant = await readModelService.getTenantById(tenantId); - if (!tenant) { - throw tenantNotFound(tenantId); + throw tenantNotAllowedToDelegation(tenant.id, kind); } }; diff --git a/packages/delegation-process/src/utilities/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts index 98e2a40d3a..00268107f2 100644 --- a/packages/delegation-process/src/utilities/errorMappers.ts +++ b/packages/delegation-process/src/utilities/errorMappers.ts @@ -12,6 +12,7 @@ const { HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_FORBIDDEN, HTTP_STATUS_UNAUTHORIZED, + HTTP_STATUS_CONFLICT, } = constants; export const getDelegationsErrorMapper = ( @@ -32,13 +33,16 @@ export const createProducerDelegationErrorMapper = ( match(error.code) .with( "eserviceNotFound", - "delegationAlreadyExists", "tenantNotFound", "invalidDelegatorAndDelegateIds", - "invalidExternalOriginId", - "tenantNotAllowedToDelegation", () => HTTP_STATUS_BAD_REQUEST ) + .with( + "tenantIsNotIPAError", + "tenantNotAllowedToDelegation", + () => HTTP_STATUS_FORBIDDEN + ) + .with("delegationAlreadyExists", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); export const revokeDelegationErrorMapper = ( diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index dc1841a5ca..69199af69a 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -1,7 +1,7 @@ /* eslint-disable functional/no-let */ import { decodeProtobufPayload, - getMockDelegationProducer, + getMockDelegation, getMockTenant, getMockEService, getMockAuthData, @@ -16,6 +16,7 @@ import { Tenant, toDelegationV2, unsafeBrandId, + delegationKind, } from "pagopa-interop-models"; import { delegationState } from "pagopa-interop-models"; import { @@ -40,7 +41,7 @@ import { flushPDFMetadata, } from "./utils.js"; -describe("approve delegation", () => { +describe("approve producer delegation", () => { const currentExecutionTime = new Date(); beforeAll(async () => { vi.useFakeTimers(); @@ -63,7 +64,8 @@ describe("approve delegation", () => { it("should approve delegation if validations succeed", async () => { const delegationId = generateId(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, id: delegationId, state: "WaitingForApproval", delegateId: delegate.id, @@ -173,7 +175,8 @@ describe("approve delegation", () => { it("should throw operationRestrictedToDelegate when approver is not the delegate", async () => { const wrongDelegate = getMockTenant(); await addOneTenant(wrongDelegate); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, delegatorId: delegator.id, @@ -194,7 +197,8 @@ describe("approve delegation", () => { }); it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "Active", delegateId: delegate.id, delegatorId: delegator.id, @@ -219,7 +223,8 @@ describe("approve delegation", () => { }); it("should generete a pdf document for a delegation", async () => { - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, delegatorId: delegator.id, diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts index 97b0cec3c2..1463fba985 100644 --- a/packages/delegation-process/test/createProducerDelegation.test.ts +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -2,7 +2,7 @@ import { fail } from "assert"; import { genericLogger } from "pagopa-interop-commons"; import { decodeProtobufPayload, - getMockDelegationProducer, + getMockDelegation, getMockEService, getMockTenant, getRandomAuthData, @@ -25,14 +25,14 @@ import { delegatorAndDelegateSameIdError, differentEServiceProducer, eserviceNotFound, - invalidExternalOriginError, + tenantIsNotIPAError, tenantNotAllowedToDelegation, tenantNotFound, } from "../src/model/domain/errors.js"; import { activeDelegationStates, - delegationNotActivableStates, + inactiveDelegationStates, } from "../src/services/validators.js"; import { addOneDelegation, @@ -81,8 +81,8 @@ const expectedDelegationCreation = async ( ); }; -describe("create delegation", () => { - it("should create a delegation if not exists", async () => { +describe("create producer delegation", () => { + it("should create a delegation if it does not exist", async () => { const currentExecutionTime = new Date(); vi.useFakeTimers(); vi.setSystemTime(currentExecutionTime); @@ -93,7 +93,7 @@ describe("create delegation", () => { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -147,83 +147,87 @@ describe("create delegation", () => { vi.useRealTimers(); }); - it("should create a delegation if already exists the same delegation in status Rejected or Revoked", async () => { - const currentExecutionTime = new Date(); - vi.useFakeTimers(); - vi.setSystemTime(currentExecutionTime); + it.each(inactiveDelegationStates)( + "should create a delegation the same delegation exists and is in state %s", + async (inactiveDelegationState) => { + const currentExecutionTime = new Date(); + vi.useFakeTimers(); + vi.setSystemTime(currentExecutionTime); - const delegatorId = generateId(); - const authData = getRandomAuthData(delegatorId); - const delegator = { - ...getMockTenant(delegatorId), - externalId: { - origin: "IPA", - value: "anythings", - }, - }; - - const delegate = { - ...getMockTenant(), - features: [ - { - type: "DelegatedProducer" as const, - availabilityTimestamp: currentExecutionTime, + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "test", }, - ], - }; - const eservice = getMockEService(generateId(), delegatorId); - - const existentDelegation = { - ...getMockDelegationProducer({ - id: generateId(), - delegatorId, - delegateId: delegate.id, - eserviceId: eservice.id, - }), - state: randomArrayItem(delegationNotActivableStates), - }; + }; - await addOneTenant(delegator); - await addOneTenant(delegate); - await addOneEservice(eservice); - await addOneDelegation(existentDelegation); + const delegate = { + ...getMockTenant(), + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: currentExecutionTime, + }, + ], + }; + const eservice = getMockEService(generateId(), delegatorId); - const actualDelegation = - await delegationProducerService.createProducerDelegation( - { + const existentDelegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, + id: generateId(), + delegatorId, delegateId: delegate.id, eserviceId: eservice.id, - }, - { - authData, - logger: genericLogger, - correlationId: generateId(), - serviceName: "DelegationServiceTest", - } - ); + }), + state: inactiveDelegationState, + }; - const expectedDelegation: Delegation = { - id: actualDelegation.id, - delegatorId, - delegateId: delegate.id, - eserviceId: eservice.id, - kind: delegationKind.delegatedProducer, - state: delegationState.waitingForApproval, - createdAt: currentExecutionTime, - submittedAt: currentExecutionTime, - stamps: { - submission: { - who: delegatorId, - when: currentExecutionTime, + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEservice(eservice); + await addOneDelegation(existentDelegation); + + const actualDelegation = + await delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: eservice.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); + + const expectedDelegation: Delegation = { + id: actualDelegation.id, + delegatorId, + delegateId: delegate.id, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + state: delegationState.waitingForApproval, + createdAt: currentExecutionTime, + submittedAt: currentExecutionTime, + stamps: { + submission: { + who: delegatorId, + when: currentExecutionTime, + }, }, - }, - }; + }; - await expectedDelegationCreation(actualDelegation, expectedDelegation); - vi.useRealTimers(); - }); + await expectedDelegationCreation(actualDelegation, expectedDelegation); + vi.useRealTimers(); + } + ); - it("should throw an differentEServiceProducer error if requester is not Eservice producer", async () => { + it("should throw a differentEServiceProducer error if requester is not Eservice producer", async () => { const currentExecutionTime = new Date(); vi.useFakeTimers(); vi.setSystemTime(currentExecutionTime); @@ -234,7 +238,7 @@ describe("create delegation", () => { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -272,7 +276,7 @@ describe("create delegation", () => { }); it.each(activeDelegationStates)( - "should throw an delegationAlreadyExists error when Delegation for eservice producer already exists with for same delegator, delegate and eserivce ", + "should throw a delegationAlreadyExists error when a producer Delegation in state %s already exists with for same delegator, delegate and eservice", async (validDelegationState) => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); @@ -280,7 +284,7 @@ describe("create delegation", () => { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -294,8 +298,9 @@ describe("create delegation", () => { ], }; const eservice = getMockEService(generateId(), delegatorId); - const existentValidDelegation = { - ...getMockDelegationProducer({ + const existentActiveDelegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, id: generateId(), delegatorId, delegateId: delegate.id, @@ -307,35 +312,40 @@ describe("create delegation", () => { await addOneTenant(delegate); await addOneTenant(delegator); await addOneEservice(eservice); - // Add existent valid delegation for the same delegator, delegate and eservice - await addOneDelegation(existentValidDelegation); - // Add existent invalid delegation for the same delegator, delegate and eservice + // Add existent active delegation for the same delegator, delegate and eservice + await addOneDelegation(existentActiveDelegation); + // Add existent inactive delegation for the same delegator, delegate and eservice await addOneDelegation({ - ...existentValidDelegation, + ...existentActiveDelegation, id: generateId(), - state: validDelegationState, + state: randomArrayItem(inactiveDelegationStates), }); // Add another generic delegation - await addOneDelegation(getMockDelegationProducer()); + await addOneDelegation( + getMockDelegation({ kind: delegationKind.delegatedProducer }) + ); // Add another delegation with same delegator await addOneDelegation( - getMockDelegationProducer({ + getMockDelegation({ + kind: delegationKind.delegatedProducer, delegatorId, }) ); // Add another delegation with same delegate await addOneDelegation( - getMockDelegationProducer({ + getMockDelegation({ + kind: delegationKind.delegatedProducer, delegateId: delegate.id, }) ); // Add another delegation for the same eservice await addOneDelegation( - getMockDelegationProducer({ + getMockDelegation({ + kind: delegationKind.delegatedProducer, eserviceId: eservice.id, }) ); @@ -356,14 +366,14 @@ describe("create delegation", () => { ).rejects.toThrowError( delegationAlreadyExists( delegatorId, - existentValidDelegation.eserviceId, + existentActiveDelegation.eserviceId, delegationKind.delegatedProducer ) ); } ); - it("should throw an tenantNotFound error if delegated tenant not exists", async () => { + it("should throw a tenantNotFound error if delegated tenant does not exist", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = getMockTenant(delegatorId); @@ -388,7 +398,7 @@ describe("create delegation", () => { ).rejects.toThrowError(tenantNotFound(delegateId)); }); - it("should throw an tenantNotFound error if delegator tenant not exists", async () => { + it("should throw a tenantNotFound error if delegator tenant does not exist", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); @@ -441,14 +451,14 @@ describe("create delegation", () => { ).rejects.toThrowError(delegatorAndDelegateSameIdError()); }); - it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + it("should throw an tenantIsNotIPAError error if delegator has externalId origin different from IPA", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { ...getMockTenant(delegatorId), externalId: { origin: "NOT_IPA", - value: "anythings", + value: "test", }, }; @@ -478,19 +488,61 @@ describe("create delegation", () => { serviceName: "DelegationServiceTest", } ) - ).rejects.toThrowError( - invalidExternalOriginError(delegator.externalId.origin) - ); + ).rejects.toThrowError(tenantIsNotIPAError(delegator, "Delegator")); }); - it("should throw an eserviceNotFound error if Eservice not exists", async () => { + it("should throw an tenantIsNotIPAError error if delegate has externalId origin different from IPA", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", + }, + }; + + const delegate = { + ...getMockTenant(), + externalId: { + origin: "NOT_IPA", + value: "test", + }, + features: [ + { + type: "DelegatedProducer" as const, + availabilityTimestamp: new Date(), + }, + ], + }; + + await addOneTenant(delegate); + await addOneTenant(delegator); + + await expect( + delegationProducerService.createProducerDelegation( + { + delegateId: delegate.id, + eserviceId: generateId(), + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ) + ).rejects.toThrowError(tenantIsNotIPAError(delegate, "Delegate")); + }); + + it("should throw an eserviceNotFound error if Eservice does not exist", async () => { + const delegatorId = generateId(); + const authData = getRandomAuthData(delegatorId); + const delegator = { + ...getMockTenant(delegatorId), + externalId: { + origin: "IPA", + value: "test", }, }; @@ -504,7 +556,8 @@ describe("create delegation", () => { ], }; const eserviceId = generateId(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, id: generateId(), delegatorId, delegateId: delegate.id, @@ -530,14 +583,14 @@ describe("create delegation", () => { ).rejects.toThrowError(eserviceNotFound(eserviceId)); }); - it("should throw an invalidExternalOriginError error if delegator has externalId origin different from IPA", async () => { + it("should throw a tenantNotAllowedToDelegation error if delegate tenant has no DelegatedProducer feature", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { ...getMockTenant(delegatorId), externalId: { origin: "IPA", - value: "anythings", + value: "test", }, }; @@ -559,6 +612,11 @@ describe("create delegation", () => { serviceName: "DelegationServiceTest", } ) - ).rejects.toThrowError(tenantNotAllowedToDelegation(delegate.id)); + ).rejects.toThrowError( + tenantNotAllowedToDelegation( + delegate.id, + delegationKind.delegatedProducer + ) + ); }); }); diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts index ddaf026c79..9a323bbaec 100644 --- a/packages/delegation-process/test/getDelegationById.test.ts +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -1,6 +1,10 @@ /* eslint-disable functional/no-let */ -import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; -import { DelegationId, generateId } from "pagopa-interop-models"; +import { getMockDelegation } from "pagopa-interop-commons-test/index.js"; +import { + DelegationId, + delegationKind, + generateId, +} from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { delegationNotFound } from "../src/model/domain/errors.js"; @@ -8,7 +12,9 @@ import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegation by id", () => { it("should get the delegation if it exists", async () => { - const delegation = getMockDelegationProducer(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + }); await addOneDelegation(delegation); @@ -21,7 +27,9 @@ describe("get delegation by id", () => { }); it("should fail with delegationNotFound", async () => { - const delegation = getMockDelegationProducer(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + }); await addOneDelegation(delegation); diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts index e7ba0c8381..0a11027266 100644 --- a/packages/delegation-process/test/getDelegations.test.ts +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -1,13 +1,19 @@ /* eslint-disable functional/no-let */ -import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { getMockDelegation } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; +import { delegationKind } from "pagopa-interop-models"; import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegations", () => { it("should get delegations", async () => { - const delegation1 = getMockDelegationProducer({ state: "Active" }); - const delegation2 = getMockDelegationProducer(); + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + state: "Active", + }); + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + }); await addOneDelegation(delegation1); await addOneDelegation(delegation2); diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index f24401386d..cf5afe2063 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -2,13 +2,14 @@ import { decodeProtobufPayload, getMockAuthData, - getMockDelegationProducer, + getMockDelegation, getMockTenant, } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it, vi } from "vitest"; import { DelegationId, ProducerDelegationRejectedV2, + delegationKind, generateId, toDelegationV2, unsafeBrandId, @@ -26,14 +27,15 @@ import { readLastDelegationEvent, } from "./utils.js"; -describe("reject delegation", () => { +describe("reject producer delegation", () => { it("should reject delegation if all validations succed", async () => { const currentExecutionTime = new Date(); vi.useFakeTimers(); vi.setSystemTime(currentExecutionTime); const delegate = getMockTenant(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, }); @@ -93,7 +95,8 @@ describe("reject delegation", () => { it("should throw operationRestrictedToDelegate when rejecter is not the delegate", async () => { const delegate = getMockTenant(); const wrongDelegate = getMockTenant(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "WaitingForApproval", delegateId: delegate.id, }); @@ -113,7 +116,8 @@ describe("reject delegation", () => { it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { const delegate = getMockTenant(); - const delegation = getMockDelegationProducer({ + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, state: "Active", delegateId: delegate.id, }); diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts index ebff03f0cb..ffc5c3c287 100644 --- a/packages/delegation-process/test/revokeProducerDelegation.test.ts +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -1,7 +1,7 @@ import { fail } from "assert"; import { decodeProtobufPayload, - getMockDelegationProducer, + getMockDelegation, getMockEService, getMockTenant, getRandomAuthData, @@ -17,6 +17,7 @@ import { EServiceId, unsafeBrandId, DelegationContractId, + delegationKind, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { @@ -102,7 +103,7 @@ const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { ]; }; -describe("revoke delegation", () => { +describe("revoke producer delegation", () => { const TEST_EXECUTION_DATE = new Date(); beforeAll(() => { @@ -138,7 +139,8 @@ describe("revoke delegation", () => { await addOneEservice(eservice); const existentDelegation: Delegation = { - ...getMockDelegationProducer({ + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, delegatorId, delegateId, }), @@ -257,7 +259,7 @@ describe("revoke delegation", () => { expect(delegationFromLastEvent).toMatchObject(expectedDelegation); }); - it("should throw an delegationNotFound if Delegation not exists", async () => { + it("should throw a delegationNotFound if Delegation does not exist", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegationId = generateId(); @@ -271,7 +273,7 @@ describe("revoke delegation", () => { ).rejects.toThrow(delegationNotFound(delegationId)); }); - it("should throw an delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { + it("should throw a delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { const currentExecutionTime = new Date(); const delegatorId = generateId(); @@ -286,7 +288,8 @@ describe("revoke delegation", () => { delegationApprovalDate.setMonth(currentExecutionTime.getMonth() - 1); const existentDelegation = { - ...getMockDelegationProducer({ + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, id: delegationId, delegateId, }), @@ -318,7 +321,7 @@ describe("revoke delegation", () => { }); it.each(notRevocableDelegationState)( - "should throw an delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", + "should throw a delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", async (notRevocableDelegationState: DelegationStateSeed) => { const currentExecutionTime = new Date(); @@ -333,7 +336,8 @@ describe("revoke delegation", () => { delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); const existentDelegation: Delegation = { - ...getMockDelegationProducer({ + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, delegatorId, delegateId, }), diff --git a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts index 48348a453f..e9aa448762 100644 --- a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts @@ -1,4 +1,7 @@ -import { getMockDelegationProducer } from "pagopa-interop-commons-test/index.js"; +import { + getMockDelegation, + randomArrayItem, +} from "pagopa-interop-commons-test/index.js"; import { DelegationEventEnvelopeV2, toDelegationV2, @@ -6,13 +9,16 @@ import { ProducerDelegationRejectedV2, ProducerDelegationRevokedV2, ProducerDelegationSubmittedV2, + delegationKind, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { handleMessageV2 } from "../src/delegationConsumerServiceV2.js"; import { delegations } from "./utils.js"; describe("Events V2", async () => { - const mockDelegation = getMockDelegationProducer(); + const mockDelegation = getMockDelegation({ + kind: randomArrayItem(Object.values(delegationKind)), + }); const mockMessage: DelegationEventEnvelopeV2 = { event_version: 2, stream_id: mockDelegation.id, From f439ec831105fa3d063f57d116cd96b312dca44a Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Fri, 22 Nov 2024 12:11:07 +0100 Subject: [PATCH 033/126] Fixing type of `who` in delegation process (#1216) --- packages/commons-test/src/testUtils.ts | 5 +++- .../src/services/delegationProducerService.ts | 13 ++++----- .../delegation-process/test/.eslintrc.json | 3 ++- .../test/approveProducerDelegation.test.ts | 15 ++++++----- .../test/createProducerDelegation.test.ts | 4 +-- .../test/rejectProducerDelegation.test.ts | 13 ++++----- .../test/revokeProducerDelegation.test.ts | 27 ++++++++++--------- packages/models/src/delegation/delegation.ts | 3 ++- 8 files changed, 46 insertions(+), 37 deletions(-) diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 72fbb57eff..6b72aa572a 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -69,6 +69,7 @@ import { makePlatformStatesClientPK, DelegationKind, unsafeBrandId, + UserId, } from "pagopa-interop-models"; import { AuthData, dateToSeconds } from "pagopa-interop-commons"; import { z } from "zod"; @@ -376,6 +377,7 @@ export const getMockDelegation = ({ delegateId = generateId(), eserviceId = generateId(), state = "WaitingForApproval", + submitterId = generateId(), activationContract = undefined, revocationContract = undefined, }: { @@ -385,6 +387,7 @@ export const getMockDelegation = ({ delegateId?: TenantId; eserviceId?: EServiceId; state?: DelegationState; + submitterId?: UserId; activationContract?: DelegationContractDocument; revocationContract?: DelegationContractDocument; }): Delegation => { @@ -403,7 +406,7 @@ export const getMockDelegation = ({ kind, stamps: { submission: { - who: delegatorId, + who: submitterId, when: creationTime, }, }, diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index 81c844f3f3..72963b4127 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -111,7 +111,7 @@ export function delegationProducerServiceBuilder( kind: delegationKind.delegatedProducer, stamps: { submission: { - who: delegatorId, + who: authData.userId, when: creationDate, }, }, @@ -166,7 +166,7 @@ export function delegationProducerServiceBuilder( stamps: { ...currentDelegation.data.stamps, revocation: { - who: delegatorId, + who: authData.userId, when: now, }, }, @@ -232,7 +232,7 @@ export function delegationProducerServiceBuilder( stamps: { ...delegation.stamps, activation: { - who: delegateId, + who: authData.userId, when: now, }, }, @@ -262,19 +262,20 @@ export function delegationProducerServiceBuilder( assertIsDelegate(delegation, delegateId); assertIsState(delegationState.waitingForApproval, delegation); + const now = new Date(); await repository.createEvent( toCreateEventProducerDelegationRejected( { data: { ...delegation, state: delegationState.rejected, - rejectedAt: new Date(), + rejectedAt: now, rejectionReason, stamps: { ...delegation.stamps, rejection: { - who: delegateId, - when: new Date(), + who: authData.userId, + when: now, }, }, }, diff --git a/packages/delegation-process/test/.eslintrc.json b/packages/delegation-process/test/.eslintrc.json index 6135a5ce08..9346af31d5 100644 --- a/packages/delegation-process/test/.eslintrc.json +++ b/packages/delegation-process/test/.eslintrc.json @@ -2,6 +2,7 @@ "extends": ["../../../.eslintrc.cjs"], "rules": { "functional/immutable-data": "off", - "sonarjs/no-identical-functions": "off" + "sonarjs/no-identical-functions": "off", + "@typescript-eslint/no-non-null-assertion": "off" } } diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index 69199af69a..1605edf592 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -4,7 +4,7 @@ import { getMockDelegation, getMockTenant, getMockEService, - getMockAuthData, + getRandomAuthData, } from "pagopa-interop-commons-test/index.js"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { @@ -63,6 +63,7 @@ describe("approve producer delegation", () => { it("should approve delegation if validations succeed", async () => { const delegationId = generateId(); + const authData = getRandomAuthData(delegate.id); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, @@ -77,7 +78,7 @@ describe("approve producer delegation", () => { expect(version).toBe("0"); await delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(delegate.id), + authData, serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -107,7 +108,7 @@ describe("approve producer delegation", () => { stamps: { ...delegation.stamps, activation: { - who: delegate.id, + who: authData.userId, when: currentExecutionTime, }, }, @@ -163,7 +164,7 @@ describe("approve producer delegation", () => { delegationProducerService.approveProducerDelegation( nonExistentDelegationId, { - authData: getMockAuthData(delegateId), + authData: getRandomAuthData(delegateId), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -186,7 +187,7 @@ describe("approve producer delegation", () => { await expect( delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(wrongDelegate.id), + authData: getRandomAuthData(wrongDelegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -208,7 +209,7 @@ describe("approve producer delegation", () => { await expect( delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(delegate.id), + authData: getRandomAuthData(delegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -235,7 +236,7 @@ describe("approve producer delegation", () => { expect(version).toBe("0"); await delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getMockAuthData(delegate.id), + authData: getRandomAuthData(delegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts index 1463fba985..f3e42b73c5 100644 --- a/packages/delegation-process/test/createProducerDelegation.test.ts +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -137,7 +137,7 @@ describe("create producer delegation", () => { submittedAt: currentExecutionTime, stamps: { submission: { - who: delegatorId, + who: authData.userId, when: currentExecutionTime, }, }, @@ -216,7 +216,7 @@ describe("create producer delegation", () => { submittedAt: currentExecutionTime, stamps: { submission: { - who: delegatorId, + who: authData.userId, when: currentExecutionTime, }, }, diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index cf5afe2063..9fd50cbd59 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -1,9 +1,9 @@ /* eslint-disable functional/no-let */ import { decodeProtobufPayload, - getMockAuthData, getMockDelegation, getMockTenant, + getRandomAuthData, } from "pagopa-interop-commons-test/index.js"; import { describe, expect, it, vi } from "vitest"; import { @@ -34,6 +34,7 @@ describe("reject producer delegation", () => { vi.setSystemTime(currentExecutionTime); const delegate = getMockTenant(); + const authData = getRandomAuthData(delegate.id); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, state: "WaitingForApproval", @@ -47,7 +48,7 @@ describe("reject producer delegation", () => { delegation.id, rejectionReason, { - authData: getMockAuthData(delegate.id), + authData, serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -67,7 +68,7 @@ describe("reject producer delegation", () => { rejectionReason, stamps: { ...delegation.stamps, - rejection: { who: delegate.id, when: currentExecutionTime }, + rejection: { who: authData.userId, when: currentExecutionTime }, }, }); expect(actualDelegation).toEqual(expectedDelegation); @@ -83,7 +84,7 @@ describe("reject producer delegation", () => { nonExistentDelegationId, "", { - authData: getMockAuthData(delegateId), + authData: getRandomAuthData(delegateId), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -104,7 +105,7 @@ describe("reject producer delegation", () => { await expect( delegationProducerService.rejectProducerDelegation(delegation.id, "", { - authData: getMockAuthData(wrongDelegate.id), + authData: getRandomAuthData(wrongDelegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, @@ -125,7 +126,7 @@ describe("reject producer delegation", () => { await expect( delegationProducerService.rejectProducerDelegation(delegation.id, "", { - authData: getMockAuthData(delegate.id), + authData: getRandomAuthData(delegate.id), serviceName: "", correlationId: generateId(), logger: genericLogger, diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts index ffc5c3c287..1130cc07c3 100644 --- a/packages/delegation-process/test/revokeProducerDelegation.test.ts +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -18,6 +18,7 @@ import { unsafeBrandId, DelegationContractId, delegationKind, + UserId, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { @@ -51,7 +52,7 @@ type DelegationStateSeed = }; stamps: { rejection: { - who: TenantId; + who: UserId; when: Date; }; }; @@ -63,7 +64,7 @@ type DelegationStateSeed = }; stamps: { revocation: { - who: TenantId; + who: UserId; when: Date; }; }; @@ -83,7 +84,7 @@ const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { }, stamps: { rejection: { - who: generateId(), + who: generateId(), when: rejectionOrRevokeDate, }, }, @@ -95,7 +96,7 @@ const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { }, stamps: { revocation: { - who: generateId(), + who: generateId(), when: rejectionOrRevokeDate, }, }, @@ -149,11 +150,11 @@ describe("revoke producer delegation", () => { submittedAt: delegationCreationDate, stamps: { submission: { - who: delegatorId, + who: generateId(), when: delegationCreationDate, }, activation: { - who: delegateId, + who: generateId(), when: delegationActivationDate, }, }, @@ -186,15 +187,15 @@ describe("revoke producer delegation", () => { revokedAt: currentExecutionTime, stamps: { submission: { - who: delegatorId, + who: existentDelegation.stamps.submission.who, when: delegationCreationDate, }, activation: { - who: delegateId, + who: existentDelegation.stamps.activation!.who, when: delegationActivationDate, }, revocation: { - who: delegatorId, + who: authData.userId, when: currentExecutionTime, }, }, @@ -297,11 +298,11 @@ describe("revoke producer delegation", () => { submittedAt: delegationCreationDate, stamps: { submission: { - who: delegatorId, + who: generateId(), when: delegationCreationDate, }, approval: { - who: delegateId, + who: generateId(), when: delegationApprovalDate, }, }, @@ -345,11 +346,11 @@ describe("revoke producer delegation", () => { submittedAt: delegationCreationDate, stamps: { submission: { - who: delegatorId, + who: generateId(), when: delegationCreationDate, }, activation: { - who: delegateId, + who: generateId(), when: delegationActivationDate, }, ...notRevocableDelegationState.stamps, diff --git a/packages/models/src/delegation/delegation.ts b/packages/models/src/delegation/delegation.ts index f9cc0dd3d6..5ad09fc983 100644 --- a/packages/models/src/delegation/delegation.ts +++ b/packages/models/src/delegation/delegation.ts @@ -4,6 +4,7 @@ import { DelegationId, EServiceId, TenantId, + UserId, } from "../brandedIds.js"; export const delegationKind = { @@ -42,7 +43,7 @@ export type DelegationContractDocument = z.infer< >; export const DelegationStamp = z.object({ - who: TenantId, + who: UserId, when: z.coerce.date(), }); export type DelegationStamp = z.infer; From cd3422bd0f85b4f90eeede138ee2545fae3ddd16 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 22 Nov 2024 12:37:07 +0100 Subject: [PATCH 034/126] PIN-5674 - Include descriptors in `Delegation` model in bff (#1199) --- packages/api-clients/open-api/bffApi.yml | 5 +++++ .../backend-for-frontend/src/api/delegationApiConverter.ts | 2 ++ 2 files changed, 7 insertions(+) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 9542323821..0cd2c52c31 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -15287,11 +15287,16 @@ components: format: uuid producerName: type: string + descriptors: + type: array + items: + $ref: "#/components/schemas/CompactDescriptor" required: - id - name - producerId - producerName + - descriptors Delegation: type: object additionalProperties: false diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts index 3fa376b4b4..d6c9144c05 100644 --- a/packages/backend-for-frontend/src/api/delegationApiConverter.ts +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -11,6 +11,7 @@ import { DelegationState, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { toCompactDescriptor } from "./catalogApiConverter.js"; export type DelegationsQueryParams = { delegatorIds?: string[]; @@ -67,6 +68,7 @@ export function toBffDelegationApiDelegation( description: eservice.description, producerId: eservice.producerId, producerName: producer.name, + descriptors: eservice.descriptors.map(toCompactDescriptor), }, delegate: { id: delegate.id, From 69e5ffc033cb69f8d85dcbb2a6c936d076d787f7 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 22 Nov 2024 17:37:04 +0100 Subject: [PATCH 035/126] Updated aws deps --- .../package.json | 4 +- packages/authorization-server/package.json | 4 +- pnpm-lock.yaml | 3639 +++++------------ 3 files changed, 1020 insertions(+), 2627 deletions(-) diff --git a/packages/authorization-platformstate-writer/package.json b/packages/authorization-platformstate-writer/package.json index 62ee6846f6..e6d9ea7e93 100644 --- a/packages/authorization-platformstate-writer/package.json +++ b/packages/authorization-platformstate-writer/package.json @@ -30,8 +30,8 @@ "vitest": "1.6.0" }, "dependencies": { - "@aws-sdk/client-dynamodb": "3.637.0", - "@aws-sdk/util-dynamodb": "3.637.0", + "@aws-sdk/client-dynamodb": "3.693.0", + "@aws-sdk/util-dynamodb": "3.693.0", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/authorization-server/package.json b/packages/authorization-server/package.json index 0fd0589788..aee65c4608 100644 --- a/packages/authorization-server/package.json +++ b/packages/authorization-server/package.json @@ -32,9 +32,9 @@ "jose": "5.9.4" }, "dependencies": { - "@aws-sdk/client-dynamodb": "3.637.0", + "@aws-sdk/client-dynamodb": "3.693.0", "@aws-sdk/client-kms": "3.600.0", - "@aws-sdk/util-dynamodb": "3.637.0", + "@aws-sdk/util-dynamodb": "3.693.0", "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", "axios": "1.7.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81acb9ffd1..dc6faaff7e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,11 +162,11 @@ importers: packages/agreement-platformstate-writer: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.637.0 - version: 3.637.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.637.0 - version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -677,11 +677,11 @@ importers: packages/authorization-platformstate-writer: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.637.0 - version: 3.637.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.637.0 - version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -811,14 +811,14 @@ importers: packages/authorization-server: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.637.0 - version: 3.637.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/client-kms': specifier: 3.600.0 version: 3.600.0 '@aws-sdk/util-dynamodb': - specifier: 3.637.0 - version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -966,11 +966,11 @@ importers: packages/backend-for-frontend: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.602.0 - version: 3.602.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.602.0 - version: 3.602.0(@aws-sdk/client-dynamodb@3.602.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -1130,11 +1130,11 @@ importers: packages/catalog-platformstate-writer: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.637.0 - version: 3.637.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.637.0 - version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1429,17 +1429,17 @@ importers: packages/commons: dependencies: '@aws-sdk/client-kms': - specifier: 3.600.0 - version: 3.600.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/client-s3': - specifier: 3.600.0 - version: 3.600.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/client-sesv2': - specifier: 3.620.1 - version: 3.620.1 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/s3-request-presigner': - specifier: 3.623.0 - version: 3.623.0 + specifier: 3.693.0 + version: 3.693.0 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -1538,14 +1538,14 @@ importers: specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) '@aws-sdk/client-dynamodb': - specifier: 3.637.0 - version: 3.637.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/client-sesv2': - specifier: 3.620.1 - version: 3.620.1 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.658.1 - version: 3.658.1(@aws-sdk/client-dynamodb@3.637.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@faker-js/faker': specifier: 8.4.1 version: 8.4.1 @@ -2044,8 +2044,8 @@ importers: packages/ivass-certified-attributes-importer: dependencies: '@aws-sdk/client-s3': - specifier: 3.387.0 - version: 3.387.0 + specifier: 3.693.0 + version: 3.693.0 adm-zip: specifier: 0.5.15 version: 0.5.15 @@ -2067,9 +2067,6 @@ importers: pagopa-interop-models: specifier: workspace:* version: link:../models - ts-pattern: - specifier: 5.2.0 - version: 5.2.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2108,14 +2105,14 @@ importers: packages/kafka-iam-auth: dependencies: '@aws-sdk/client-sso-oidc': - specifier: 3.609.0 - version: 3.609.0(@aws-sdk/client-sts@3.609.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/client-sts': - specifier: 3.609.0 - version: 3.609.0 + specifier: 3.693.0 + version: 3.693.0 aws-msk-iam-sasl-signer-js: specifier: 1.0.0 - version: 1.0.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + version: 1.0.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -2228,8 +2225,8 @@ importers: packages/notifier-seeder: dependencies: '@aws-sdk/client-sqs': - specifier: 3.600.0 - version: 3.600.0 + specifier: 3.693.0 + version: 3.693.0 '@protobuf-ts/plugin': specifier: 2.9.4 version: 2.9.4 @@ -2292,11 +2289,11 @@ importers: packages/one-trust-notices: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.648.0 - version: 3.648.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.648.0 - version: 3.648.0(@aws-sdk/client-dynamodb@3.648.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) axios: specifier: ^1.7.4 version: 1.7.4 @@ -2338,8 +2335,8 @@ importers: packages/pn-consumers: dependencies: '@aws-sdk/client-s3': - specifier: 3.387.0 - version: 3.387.0 + specifier: 3.693.0 + version: 3.693.0 axios: specifier: 1.7.4 version: 1.7.4 @@ -2607,11 +2604,11 @@ importers: packages/purpose-platformstate-writer: dependencies: '@aws-sdk/client-dynamodb': - specifier: 3.637.0 - version: 3.637.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': - specifier: 3.637.0 - version: 3.637.0(@aws-sdk/client-dynamodb@3.637.0) + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3058,37 +3055,19 @@ packages: peerDependencies: openapi-types: '>=7' - '@aws-crypto/crc32@3.0.0': - resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==} - '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} engines: {node: '>=16.0.0'} - '@aws-crypto/crc32c@3.0.0': - resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==} - '@aws-crypto/crc32c@5.2.0': resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} - '@aws-crypto/ie11-detection@3.0.0': - resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==} - - '@aws-crypto/sha1-browser@3.0.0': - resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==} - '@aws-crypto/sha1-browser@5.2.0': resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} - '@aws-crypto/sha256-browser@3.0.0': - resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==} - '@aws-crypto/sha256-browser@5.2.0': resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} - '@aws-crypto/sha256-js@3.0.0': - resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==} - '@aws-crypto/sha256-js@4.0.0': resolution: {integrity: sha512-MHGJyjE7TX9aaqXj7zk2ppnFUOhaDs5sP+HtNS0evOxn72c+5njUmyJmpGd7TfyoDznZlHMmdo/xGUdu2NIjNQ==} @@ -3096,15 +3075,9 @@ packages: resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} engines: {node: '>=16.0.0'} - '@aws-crypto/supports-web-crypto@3.0.0': - resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==} - '@aws-crypto/supports-web-crypto@5.2.0': resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} - '@aws-crypto/util@3.0.0': - resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==} - '@aws-crypto/util@4.0.0': resolution: {integrity: sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ==} @@ -3115,36 +3088,28 @@ packages: resolution: {integrity: sha512-3kDTpia1iN/accayoH3MbZRbDvX2tzrKrBTU7wNNoazVrh+gOMS8KCOWrOB72F0V299l4FsfQhnl9BDMVrc1iw==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-dynamodb@3.602.0': - resolution: {integrity: sha512-q7lH7YD9KvHLF3tyAG1UqaPv4a6KiHLunqKYh8vt3d1WJK7t4wzE97Vf19MfNpza1MuZ0OF/SK8Kl69vEMrtOA==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/client-dynamodb@3.637.0': - resolution: {integrity: sha512-zUneT0yLgJjC69yry2fgYVWkv68OeV3amWaDXHirA8yJgygyc7tBLo+sQmtHczmKt8dBD9bU3OWpbAbtpF9Esw==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/client-dynamodb@3.648.0': - resolution: {integrity: sha512-61yU6wQRlwOhD0mfJS/N8SYmv9hxkVYGKsXqSJ5PNNnySutoNof7cmX8cTuijpTQqLL9sKPfvPMlJCv7/M1AiA==} + '@aws-sdk/client-dynamodb@3.693.0': + resolution: {integrity: sha512-EmgFoE/wAxiOq/sfO/VFGlmvfq0FexUO4IMURr3deIpU/AuCsuU87HJH/UodFdKu88ykNZxMfHHku6o6BV2dAA==} engines: {node: '>=16.0.0'} '@aws-sdk/client-kms@3.600.0': resolution: {integrity: sha512-m1o8aiVrVjExw6O+8JszXV3hr8sCyXKOLq1WCwWJqYF6Uf4vCf8iTYISQB3skbKUnBJm4SxVA82iViGAtWB7JA==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-s3@3.387.0': - resolution: {integrity: sha512-TX42MDfXnIy/U6f8XjCTR1Ezg1125Sv5k9kdKZJ0kkKcb/81N0+RfTqsR+Kpn/AbLjtvSUWrFIdc1o//GsfZAQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-kms@3.693.0': + resolution: {integrity: sha512-9APrRDbScYqfCG89Trruf9+Bx39wlXQdwPEhluycpCF+bHYiTKdIXc4uix6hjbXzK0NKbgitus8bbMvsHX+Oxg==} + engines: {node: '>=16.0.0'} - '@aws-sdk/client-s3@3.600.0': - resolution: {integrity: sha512-iYoKbJTputbf+ubkX6gSK/y/4uJEBRaXZ18jykLdBQ8UJuGrk2gqvV8h7OlGAhToCeysmmMqM0vDWyLt6lP8nw==} + '@aws-sdk/client-s3@3.693.0': + resolution: {integrity: sha512-vgGI2e0Q6pzyhqfrSysi+sk/i+Nl+lMon67oqj/57RcCw9daL1/inpS+ADuwHpiPWkrg+U0bOXnmHjkLeTslJg==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sesv2@3.620.1': - resolution: {integrity: sha512-pu/CbRQuxCA0EmDqyAktc77pjbmeWuQao6aLDBuXLDbccwKgRECLCTscqUcnqtfFhPkiOAim9LdczqEXLNIpcA==} + '@aws-sdk/client-sesv2@3.693.0': + resolution: {integrity: sha512-5lQUWTxNsd933o1TvAsa564BeMv8IbhxHIgssCxf3ibnqUgI7S/R6Bk62KqlxjxcgWDXabQWQskejZ8cpupYqA==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sqs@3.600.0': - resolution: {integrity: sha512-GgjEiWbGbiHGU3yZcCr1hXfaq/B/3ncYclqLEbbxrWkQOdri3qfl278h+Qn0/DQ8On0kj1UUxld87TVIkYfG8w==} + '@aws-sdk/client-sqs@3.693.0': + resolution: {integrity: sha512-Nfmo6bgad26PP1B7MZP9kpxOw874cVEvJHXBXwkSLg6ZPAeU2cnvTEE++omHkzLeeB9MbpmvhQtdaUn4c7DYZQ==} engines: {node: '>=16.0.0'} '@aws-sdk/client-sso-oidc@3.600.0': @@ -3157,27 +3122,11 @@ packages: peerDependencies: '@aws-sdk/client-sts': ^3.609.0 - '@aws-sdk/client-sso-oidc@3.620.1': - resolution: {integrity: sha512-gm69ttbkr7Kbg/Zzr3SczyLWkLgmK3bEZtkvbM/40ZW5ItYhDzJE48Ovs2lyA64h2YsOftDqqwcbJirAAdTgSg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 - - '@aws-sdk/client-sso-oidc@3.637.0': - resolution: {integrity: sha512-27bHALN6Qb6m6KZmPvRieJ/QRlj1lyac/GT2Rn5kJpre8Mpp+yxrtvp3h9PjNBty4lCeFEENfY4dGNSozBuBcw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.637.0 - - '@aws-sdk/client-sso-oidc@3.645.0': - resolution: {integrity: sha512-X9ULtdk3cO+1ysurEkJ1MSnu6U00qodXx+IVual+1jXX4RYY1WmQmfo7uDKf6FFkz7wW1DAqU+GJIBNQr0YH8A==} + '@aws-sdk/client-sso-oidc@3.693.0': + resolution: {integrity: sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 - - '@aws-sdk/client-sso@3.387.0': - resolution: {integrity: sha512-E7uKSvbA0XMKSN5KLInf52hmMpe9/OKo6N9OPffGXdn3fNEQlvyQq3meUkqG7Is0ldgsQMz5EUBNtNybXzr3tQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sts': ^3.693.0 '@aws-sdk/client-sso@3.598.0': resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==} @@ -3187,22 +3136,10 @@ packages: resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sso@3.620.1': - resolution: {integrity: sha512-4Ox0BSs+atrAhLvjNHN2uiYvSTdpMv//IS4l4XRoQG0cJKIPLs3OU3PL5H0X1NfZehz9/8FTWl5Lv81uw4j1eA==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/client-sso@3.637.0': - resolution: {integrity: sha512-+KjLvgX5yJYROWo3TQuwBJlHCY0zz9PsLuEolmXQn0BVK1L/m9GteZHtd+rEdAoDGBpE0Xqjy1oz5+SmtsaRUw==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/client-sso@3.645.0': - resolution: {integrity: sha512-2rc8TjnsNddOeKQ/pfNN7deNvGLXAeKeYtHtGDAiM2qfTKxd2sNcAsZ+JCDLyshuD4xLM5fpUyR0X8As9EAouQ==} + '@aws-sdk/client-sso@3.693.0': + resolution: {integrity: sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sts@3.387.0': - resolution: {integrity: sha512-RPOME0gpAViheH6xHyMg/XkE1G/fs6dgKK/NqlBZDjwMsSTPc8CmItEC6FOsCaLJktif0tD/u9m2uaQ4Lb1nVw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/client-sts@3.600.0': resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} engines: {node: '>=16.0.0'} @@ -3211,16 +3148,8 @@ packages: resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sts@3.620.1': - resolution: {integrity: sha512-d+ECGFDg0IsDdmfKU2O0VeMYKZcmbfBaA9HkZnZ39wu1BlXGI73xJe8cfmzbobvu+Ly+bAfHdLCpgIY+pD4D7g==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/client-sts@3.637.0': - resolution: {integrity: sha512-xUi7x4qDubtA8QREtlblPuAcn91GS/09YVEY/RwU7xCY0aqGuFwgszAANlha4OUIqva8oVj2WO4gJuG+iaSnhw==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/client-sts@3.645.0': - resolution: {integrity: sha512-6azXYtvtnAsPf2ShN9vKynIYVcJOpo6IoVmoMAVgNaBJyllP+s/RORzranYZzckqfmrudSxtct4rVapjLWuAMg==} + '@aws-sdk/client-sts@3.693.0': + resolution: {integrity: sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==} engines: {node: '>=16.0.0'} '@aws-sdk/core@3.598.0': @@ -3231,22 +3160,14 @@ packages: resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} engines: {node: '>=16.0.0'} - '@aws-sdk/core@3.620.1': - resolution: {integrity: sha512-6Ejce93dDlDnovl6oYtxj3I/SJMOQoFdmmtM4+4W/cgMWH+l00T5aszVxDLjjPfu3Ryt7dNhrXaYeK2Ue1ZBmg==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/core@3.635.0': - resolution: {integrity: sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==} + '@aws-sdk/core@3.693.0': + resolution: {integrity: sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==} engines: {node: '>=16.0.0'} '@aws-sdk/credential-provider-cognito-identity@3.609.0': resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-env@3.387.0': - resolution: {integrity: sha512-PVqNk7XPIYe5CMYNvELkcALtkl/pIM8/uPtqEtTg+mgnZBeL4fAmgXZiZMahQo1DxP5t/JaK384f6JG+A0qDjA==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-env@3.598.0': resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==} engines: {node: '>=16.0.0'} @@ -3255,8 +3176,8 @@ packages: resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-env@3.620.1': - resolution: {integrity: sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==} + '@aws-sdk/credential-provider-env@3.693.0': + resolution: {integrity: sha512-hMUZaRSF7+iBKZfBHNLihFs9zvpM1CB8MBOTnTp5NGCVkRYF3SB2LH+Kcippe0ats4qCyB1eEoyQX99rERp2iQ==} engines: {node: '>=16.0.0'} '@aws-sdk/credential-provider-http@3.598.0': @@ -3267,18 +3188,10 @@ packages: resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-http@3.620.0': - resolution: {integrity: sha512-BI2BdrSKDmB/2ouB/NJR0PT0x/+5fmoF6XOE78hFBb4F5w/yynGgcJY936dF+oREfpME6ehjB2b0okGg78Scpw==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/credential-provider-http@3.635.0': - resolution: {integrity: sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==} + '@aws-sdk/credential-provider-http@3.693.0': + resolution: {integrity: sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-ini@3.387.0': - resolution: {integrity: sha512-DS2Jg5E4Hd9fhJqTVNBG3SEwLwcyguPDcXSVCDz5pEHlYFM1U4x9b7aAbutzZujTH99MZ6Gua8kAotB/qjEjtw==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-ini@3.598.0': resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==} engines: {node: '>=16.0.0'} @@ -3291,27 +3204,11 @@ packages: peerDependencies: '@aws-sdk/client-sts': ^3.609.0 - '@aws-sdk/credential-provider-ini@3.620.1': - resolution: {integrity: sha512-m9jwigMPRlRRhoPxCQZMOwQUd6imEJbksF6tSMYNae76DIvrCi4z2Jhp6RJ9Mij8cnewUZCAmvu2FlK9+n9M7A==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.620.1 - - '@aws-sdk/credential-provider-ini@3.637.0': - resolution: {integrity: sha512-h+PFCWfZ0Q3Dx84SppET/TFpcQHmxFW8/oV9ArEvMilw4EBN+IlxgbL0CnHwjHW64szcmrM0mbebjEfHf4FXmw==} + '@aws-sdk/credential-provider-ini@3.693.0': + resolution: {integrity: sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.637.0 - - '@aws-sdk/credential-provider-ini@3.645.0': - resolution: {integrity: sha512-LlZW0qwUwNlTaAIDCNpLbPsyXvS42pRIwF92fgtCQedmdnpN3XRUC6hcwSYI7Xru3GGKp3RnceOvsdOaRJORsw==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.645.0 - - '@aws-sdk/credential-provider-node@3.387.0': - resolution: {integrity: sha512-NviQ0EqigPWwX4avKheRzE2R4YPzO6qzdyxKZUookr+uTWYroQ4ePZbHK1/BD8LlqKKBlttX/d3ENXjynU4clA==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sts': ^3.693.0 '@aws-sdk/credential-provider-node@3.600.0': resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==} @@ -3321,22 +3218,10 @@ packages: resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-node@3.620.1': - resolution: {integrity: sha512-KaprIJW2azM+oTIHi7S1ayJ3oQqoFwpMBWFpZM1nvSzaPucrZIUmX2m4uVrMM4LfXsfUsgMkrme2rBI1fGAjCg==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/credential-provider-node@3.637.0': - resolution: {integrity: sha512-yoEhoxJJfs7sPVQ6Is939BDQJZpZCoUgKr/ySse4YKOZ24t4VqgHA6+wV7rYh+7IW24Rd91UTvEzSuHYTlxlNA==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/credential-provider-node@3.645.0': - resolution: {integrity: sha512-eGFFuNvLeXjCJf5OCIuSEflxUowmK+bCS+lK4M8ofsYOEGAivdx7C0UPxNjHpvM8wKd8vpMl5phTeS9BWX5jMQ==} + '@aws-sdk/credential-provider-node@3.693.0': + resolution: {integrity: sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-process@3.387.0': - resolution: {integrity: sha512-tQScLHmDlqkQN+mqw4s3cxepEUeHYDhFl5eH+J8puvPqWjXMYpCEdY79SAtWs6SZd4CWiZ0VLeYU6xQBZengbQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-process@3.598.0': resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==} engines: {node: '>=16.0.0'} @@ -3345,14 +3230,10 @@ packages: resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-process@3.620.1': - resolution: {integrity: sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==} + '@aws-sdk/credential-provider-process@3.693.0': + resolution: {integrity: sha512-cvxQkrTWHHjeHrPlj7EWXPnFSq8x7vMx+Zn1oTsMpCY445N9KuzjfJTkmNGwU2GT6rSZI9/0MM02aQvl5bBBTQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-sso@3.387.0': - resolution: {integrity: sha512-sQgrEyTSrwLe8zgjP9VEUDz3dtGXSCc4k00bCwODbzdOWCA1nz9oF2tFmgjFsb1Q80pae01Pe50Esix5z2eHsQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-sso@3.598.0': resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==} engines: {node: '>=16.0.0'} @@ -3361,22 +3242,10 @@ packages: resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-sso@3.620.1': - resolution: {integrity: sha512-cFU8e6ctdkWR8BRCnHFzs37N+ilbHf1OT2EeMjt1ZDE9FgTD5L5BTgVWDxnPmyQnEoBs1p4PyNPHkpHY5EmswQ==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/credential-provider-sso@3.637.0': - resolution: {integrity: sha512-Mvz+h+e62/tl+dVikLafhv+qkZJ9RUb8l2YN/LeKMWkxQylPT83CPk9aimVhCV89zth1zpREArl97+3xsfgQvA==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/credential-provider-sso@3.645.0': - resolution: {integrity: sha512-d6XuChAl5NCsCrUexc6AFb4efPmb9+66iwPylKG+iMTMYgO1ackfy1Q2/f35jdn0jolkPkzKsVyfzsEVoID6ew==} + '@aws-sdk/credential-provider-sso@3.693.0': + resolution: {integrity: sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-web-identity@3.387.0': - resolution: {integrity: sha512-6ueMPl+J3KWv6ZaAWF4Z138QCuBVFZRVAgwbtP3BNqWrrs4Q6TPksOQJ79lRDMpv0EUoyVl04B6lldNlhN8RdA==} - engines: {node: '>=14.0.0'} - '@aws-sdk/credential-provider-web-identity@3.598.0': resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==} engines: {node: '>=16.0.0'} @@ -3389,56 +3258,36 @@ packages: peerDependencies: '@aws-sdk/client-sts': ^3.609.0 - '@aws-sdk/credential-provider-web-identity@3.621.0': - resolution: {integrity: sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==} + '@aws-sdk/credential-provider-web-identity@3.693.0': + resolution: {integrity: sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.621.0 + '@aws-sdk/client-sts': ^3.693.0 '@aws-sdk/credential-providers@3.609.0': resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} engines: {node: '>=16.0.0'} - '@aws-sdk/endpoint-cache@3.572.0': - resolution: {integrity: sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/middleware-bucket-endpoint@3.387.0': - resolution: {integrity: sha512-o7Dsq0YTUHFcKXD6+30/fXv/Wzdxqz9WonhCu3ZFPwTDLZgOM4QDDKW8EcC1SplKP1IUyaEli8Affodag9T1cQ==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-bucket-endpoint@3.598.0': - resolution: {integrity: sha512-PM7BcFfGUSkmkT6+LU9TyJiB4S8yI7dfuKQDwK5ZR3P7MKaK4Uj4yyDiv0oe5xvkF6+O2+rShj+eh8YuWkOZ/Q==} + '@aws-sdk/endpoint-cache@3.693.0': + resolution: {integrity: sha512-/zK0ZZncBf5FbTfo8rJMcQIXXk4Ibhe5zEMiwFNivVPR2uNC0+oqfwXz7vjxwY0t6BPE3Bs4h9uFEz4xuGCY6w==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-endpoint-discovery@3.598.0': - resolution: {integrity: sha512-TaFo3rfapVP0FiddH2zDyA5R5XNk2M+zMeUZaBRveYamSQ11F+fMGcedBgbOsv7yNESvaZvjlcw2K+cx3jOchA==} + '@aws-sdk/middleware-bucket-endpoint@3.693.0': + resolution: {integrity: sha512-cPIa+lxMYiFRHtxKfNIVSFGO6LSgZCk42pu3d7KGwD6hu6vXRD5B2/DD3rPcEH1zgl2j0Kx1oGAV7SRXKHSFag==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-endpoint-discovery@3.620.0': - resolution: {integrity: sha512-T6kuydHBF4BPP5CVH53Fze7c2b9rqxWP88XrGtmNMXXdY4sXur1v/itGdS2l3gqRjxKo0LsmjmuQm9zL4vGneQ==} + '@aws-sdk/middleware-endpoint-discovery@3.693.0': + resolution: {integrity: sha512-OG8WM8OzYuAt3Ueb8TZoBgA+vqNgPaXksHhiy8SFTQxNamSMMRvKrDSBbdUuV96mq0lcJq1mFgJ4oRXJ1HPh6A==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-expect-continue@3.387.0': - resolution: {integrity: sha512-w415a4tjQc6a7isq0AEDWFBC0HWUCHXEDjDl94UACxfMmS9bVabuf04t9CQ+nBBVs6HdiNdfdc/pBR2pRwx2Yg==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-expect-continue@3.598.0': - resolution: {integrity: sha512-ZuHW18kaeHR8TQyhEOYMr8VwiIh0bMvF7J1OTqXHxDteQIavJWA3CbfZ9sgS4XGtrBZDyHJhjZKeCfLhN2rq3w==} + '@aws-sdk/middleware-expect-continue@3.693.0': + resolution: {integrity: sha512-MuK/gsJWpHz6Tv0CqTCS+QNOxLa2RfPh1biVCu/uO3l7kA0TjQ/C+tfgKvLXeH103tuDrOVINK+bt2ENmI3SWg==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.387.0': - resolution: {integrity: sha512-QlH97rrKlcMyLG+2ps7+DtBHfPyRIpi7sD3y0iko2u3PGXk+PoLPK8wWyGql9sFopOYTl6/Jh2Rb1b6z6NbjEA==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-flexible-checksums@3.598.0': - resolution: {integrity: sha512-xukAzds0GQXvMEY9G6qt+CzwVzTx8NyKKh04O2Q+nOch6QQ8Rs+2kTRy3Z4wQmXq2pK9hlOWb5nXA7HWpmz6Ng==} + '@aws-sdk/middleware-flexible-checksums@3.693.0': + resolution: {integrity: sha512-xkS6zjuE11ob93H9t65kHzphXcUMnN2SmIm2wycUPg+hi8Q6DJA6U2p//6oXkrr9oHy1QvwtllRd7SAd63sFKQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-host-header@3.387.0': - resolution: {integrity: sha512-EWm9PXSr8dSp7hnRth1U7OfelXQp9dLf1yS1kUL+UhppYDJpjhdP7ql3NI4xJKw8e76sP2FuJYEuzWnJHuWoyQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-host-header@3.598.0': resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==} engines: {node: '>=16.0.0'} @@ -3447,22 +3296,14 @@ packages: resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-host-header@3.620.0': - resolution: {integrity: sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==} + '@aws-sdk/middleware-host-header@3.693.0': + resolution: {integrity: sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-location-constraint@3.387.0': - resolution: {integrity: sha512-Ipdry2V58CpDcWD0ZTz6yFtpTASEBxbuWdqUUYW7pOkZ/5GPGH8NhBky7M38iGqAO6FNysvWEVCUpIqNGkI1lw==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-location-constraint@3.598.0': - resolution: {integrity: sha512-8oybQxN3F1ISOMULk7JKJz5DuAm5hCUcxMW9noWShbxTJuStNvuHf/WLUzXrf8oSITyYzIHPtf8VPlKR7I3orQ==} + '@aws-sdk/middleware-location-constraint@3.693.0': + resolution: {integrity: sha512-eDAExTZ9uNIP7vs2JCVCOuWJauGueisBSn+Ovt7UvvuEUp6KOIJqn8oFxWmyUQu2GvbG4OcaTLgbqD95YHTB0Q==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-logger@3.387.0': - resolution: {integrity: sha512-FjAvJr1XyaInT81RxUwgifnbXoFJrRBFc64XeFJgFanGIQCWLYxRrK2HV9eBpao/AycbmuoHgLd/f0sa4hZFoQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-logger@3.598.0': resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==} engines: {node: '>=16.0.0'} @@ -3471,9 +3312,9 @@ packages: resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-recursion-detection@3.387.0': - resolution: {integrity: sha512-ZF45T785ru8OwvYZw6awD9Z76OwSMM1eZzj2eY+FDz1cHfkpLjxEiti2iIH1FxbyK7n9ZqDUx29lVlCv238YyQ==} - engines: {node: '>=14.0.0'} + '@aws-sdk/middleware-logger@3.693.0': + resolution: {integrity: sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==} + engines: {node: '>=16.0.0'} '@aws-sdk/middleware-recursion-detection@3.598.0': resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==} @@ -3483,50 +3324,22 @@ packages: resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-recursion-detection@3.620.0': - resolution: {integrity: sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.387.0': - resolution: {integrity: sha512-OIUBDzGhglI6KjXVwPLh7hRbrfCpSTwWRkbXbLrPgZZuzWMoJJ3q59RVkpLnAV9Mdkg6+YA6JTw4k4hcmJblVw==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.598.0': - resolution: {integrity: sha512-5AGtLAh9wyK6ANPYfaKTqJY1IFJyePIxsEbxa7zS6REheAqyVmgJFaGu3oQ5XlxfGr5Uq59tFTRkyx26G1HkHA==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/middleware-sdk-s3@3.622.0': - resolution: {integrity: sha512-tX9wZ2ALx5Ez4bkY+SvSj6DpNZ6TmY4zlsVsdgV95LZFLjNwqnZkKkS+uKnsIyLBiBp6g92JVQwnUEIp7ov2Zw==} + '@aws-sdk/middleware-recursion-detection@3.693.0': + resolution: {integrity: sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-sdk-sqs@3.598.0': - resolution: {integrity: sha512-BVHR5cKwxXTovezHHPzP7iSNZQdMp+Pn9l2zVfFzryE2Enahkg5oxgUcLD2jeFx14QxS1k7czIEIvKh991CWsg==} + '@aws-sdk/middleware-sdk-s3@3.693.0': + resolution: {integrity: sha512-5A++RBjJ3guyq5pbYs+Oq5hMlA8CK2OWaHx09cxVfhHWl/RoaY8DXrft4gnhoUEBrrubyMw7r9j7RIMLvS58kg==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-sdk-sts@3.387.0': - resolution: {integrity: sha512-7ZzRKOJ4V/JDQmKz9z+FjZqw59mrMATEMLR6ff0H0JHMX0Uk5IX8TQB058ss+ar14qeJ4UcteYzCqHNI0O1BHw==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-signing@3.387.0': - resolution: {integrity: sha512-oJXlE0MES8gxNLo137PPNNiOICQGOaETTvq3kBSJgb/gtEAxQajMIlaNT7s1wsjOAruFHt4975nCXuY4lpx7GQ==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-signing@3.598.0': - resolution: {integrity: sha512-XKb05DYx/aBPqz6iCapsCbIl8aD8EihTuPCs51p75QsVfbQoVr4TlFfIl5AooMSITzojdAQqxt021YtvxjtxIQ==} + '@aws-sdk/middleware-sdk-sqs@3.693.0': + resolution: {integrity: sha512-txpz7gKX6b8NE+VdIy3UQzEXCNM0ubpuaEmkr1yReNh0x8hqslxtQyjhn6tqKvAABQ/VnKSJbbqMzsi710fR4Q==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-ssec@3.387.0': - resolution: {integrity: sha512-Jtie1gqqcs7ZuYDlz/kuI3CKCXoCL5Ov/Gj5X8/XmwrQJEpuB6z0KY5H1qY0xo+jtAhC8nDPv0GnuLoOfn85hw==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/middleware-ssec@3.598.0': - resolution: {integrity: sha512-f0p2xP8IC1uJ5e/tND1l81QxRtRFywEdnbtKCE0H6RSn4UIt2W3Dohe1qQDbnh27okF0PkNW6BJGdSAz3p7qbA==} + '@aws-sdk/middleware-ssec@3.693.0': + resolution: {integrity: sha512-Ro5vzI7SRgEeuoMk3fKqFjGv6mG4c7VsSCDwnkiasmafQFBTPvUIpgmu2FXMHqW/OthvoiOzpSrlJ9Bwlx2f8A==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-user-agent@3.387.0': - resolution: {integrity: sha512-hTfFTwDtp86xS98BKa+RFuLfcvGftxwzrbZeisZV8hdb4ZhvNXjSxnvM3vetW0GUEnY9xHPSGyp2ERRTinPKFQ==} - engines: {node: '>=14.0.0'} - '@aws-sdk/middleware-user-agent@3.598.0': resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==} engines: {node: '>=16.0.0'} @@ -3535,16 +3348,8 @@ packages: resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-user-agent@3.620.0': - resolution: {integrity: sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/middleware-user-agent@3.637.0': - resolution: {integrity: sha512-EYo0NE9/da/OY8STDsK2LvM4kNa79DBsf4YVtaG4P5pZ615IeFsD8xOHZeuJmUrSMlVQ8ywPRX7WMucUybsKug==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/middleware-user-agent@3.645.0': - resolution: {integrity: sha512-NpTAtqWK+49lRuxfz7st9for80r4NriCMK0RfdJSoPFVntjsSQiQ7+2nW2XL05uVY633e9DvCAw8YatX3zd1mw==} + '@aws-sdk/middleware-user-agent@3.693.0': + resolution: {integrity: sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==} engines: {node: '>=16.0.0'} '@aws-sdk/region-config-resolver@3.598.0': @@ -3555,35 +3360,18 @@ packages: resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} engines: {node: '>=16.0.0'} - '@aws-sdk/region-config-resolver@3.614.0': - resolution: {integrity: sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/s3-request-presigner@3.623.0': - resolution: {integrity: sha512-xdY7x4GQ3jVhkge0I8P2V/18p2unP3AD0m1zvacgFmxZ8tptjVpEg2fwR39gKv3pfri0DdfiPDrVONsPC2KlLw==} + '@aws-sdk/region-config-resolver@3.693.0': + resolution: {integrity: sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==} engines: {node: '>=16.0.0'} - '@aws-sdk/signature-v4-multi-region@3.387.0': - resolution: {integrity: sha512-SGuUbEFi8BXYVv4M7Hc0488I7uZbTVBW19j/B7bnyfbKFrndBXM366s/mChx4iELtESQ61AAstyafx5nGj5tIg==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@aws-sdk/signature-v4-crt': ^3.118.0 - peerDependenciesMeta: - '@aws-sdk/signature-v4-crt': - optional: true - - '@aws-sdk/signature-v4-multi-region@3.598.0': - resolution: {integrity: sha512-1r/EyTrO1gSa1FirnR8V7mabr7gk+l+HkyTI0fcTSr8ucB7gmYyW6WjkY8JCz13VYHFK62usCEDS7yoJoJOzTA==} + '@aws-sdk/s3-request-presigner@3.693.0': + resolution: {integrity: sha512-I/TCM43kZn1xb+EWMAjkcisDVrq3mYsu0ZFP81J9K/PM6n3s9bK04jaY56c3pCl6btigIOHhreutYSRRBJsCDw==} engines: {node: '>=16.0.0'} - '@aws-sdk/signature-v4-multi-region@3.622.0': - resolution: {integrity: sha512-K7ddofVNzwTFRjmLZLfs/v+hiE9m5LguajHk8WULxXQgkcDI3nPgOfmMMGuslYohaQhRwW+ic+dzYlateLUudQ==} + '@aws-sdk/signature-v4-multi-region@3.693.0': + resolution: {integrity: sha512-s7zbbsoVIriTR4ZGaateKuTqz6ddpazAyHvjk7I9kd+NvGNPiuAI18UdbuiiRI6K5HuYKf1ah6mKWFGPG15/kQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/token-providers@3.387.0': - resolution: {integrity: sha512-W9lPW6zR8yrfvDDLJnKCvHs2KwmydSo+1bG5i6WzFnY3aeOgPBJO2eDIJajZG8Q/L++ZwDaNDLL+ROnIMcg6GA==} - engines: {node: '>=14.0.0'} - '@aws-sdk/token-providers@3.598.0': resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==} engines: {node: '>=16.0.0'} @@ -3596,15 +3384,11 @@ packages: peerDependencies: '@aws-sdk/client-sso-oidc': ^3.609.0 - '@aws-sdk/token-providers@3.614.0': - resolution: {integrity: sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==} + '@aws-sdk/token-providers@3.693.0': + resolution: {integrity: sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.614.0 - - '@aws-sdk/types@3.387.0': - resolution: {integrity: sha512-YTjFabNwjTF+6yl88f0/tWff018qmmgMmjlw45s6sdVKueWxdxV68U7gepNLF2nhaQPZa6FDOBoA51NaviVs0Q==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-sso-oidc': ^3.693.0 '@aws-sdk/types@3.598.0': resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==} @@ -3618,41 +3402,19 @@ packages: resolution: {integrity: sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-arn-parser@3.310.0': - resolution: {integrity: sha512-jL8509owp/xB9+Or0pvn3Fe+b94qfklc2yPowZZIFAkFcCSIdkIglz18cPDWnYAcy9JGewpMS1COXKIUhZkJsA==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/util-arn-parser@3.568.0': - resolution: {integrity: sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/util-dynamodb@3.602.0': - resolution: {integrity: sha512-0QsRLE4cK0h9jseCbaBZpcLzBcOgPMdsEy4wIYvcXivHIdgt/JQxs329NF7EfWQU8h3PU5hRy3tiTBLuB4TmgQ==} + '@aws-sdk/types@3.692.0': + resolution: {integrity: sha512-RpNvzD7zMEhiKgmlxGzyXaEcg2khvM7wd5sSHVapOcrde1awQSOMGI4zKBQ+wy5TnDfrm170ROz/ERLYtrjPZA==} engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.602.0 - - '@aws-sdk/util-dynamodb@3.637.0': - resolution: {integrity: sha512-C2q8HcGRiahtf46Mhaqydh1gofeksj7m74PJXHYKW+pKBMLPlpou1+w2o5QSpVEp0dSBtKw30eRVQzxhqg/ACA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.637.0 - '@aws-sdk/util-dynamodb@3.648.0': - resolution: {integrity: sha512-w8cF5Ap8AL6VvA8bIbDNnrfpVvN3klsZRQ/QLVAhW1k3R3t9L+eKzoS3bBTVeyBlIh/eyXnSkQ8eduehS82FMw==} + '@aws-sdk/util-arn-parser@3.693.0': + resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.648.0 - '@aws-sdk/util-dynamodb@3.658.1': - resolution: {integrity: sha512-lzlnis+35a2OhGZlVJvM3/30iIVoP2cIv5Bkw1F2nkM6Pr+1NOd3XvYhCY1Ud5zWtV6HUSptzessvUPqJTMfjQ==} + '@aws-sdk/util-dynamodb@3.693.0': + resolution: {integrity: sha512-lwJnlQndVS7cGN1UmtG4s6IdVW3U/WheJ14Xc/mUeAH3vga7GTY3hGi+Jp1TbnaYQkad589tU+NQ5KUW7V8ZjA==} engines: {node: '>=16.0.0'} peerDependencies: - '@aws-sdk/client-dynamodb': ^3.658.1 - - '@aws-sdk/util-endpoints@3.387.0': - resolution: {integrity: sha512-g7kvuCXehGXHHBw9PkSQdwVyDFmNUZLmfrRmqMyrMDG9QLQrxr4pyWcSaYgTE16yUzhQQOR+QSey+BL6W9/N6g==} - engines: {node: '>=14.0.0'} + '@aws-sdk/client-dynamodb': ^3.693.0 '@aws-sdk/util-endpoints@3.598.0': resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==} @@ -3662,43 +3424,30 @@ packages: resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-endpoints@3.614.0': - resolution: {integrity: sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/util-endpoints@3.637.0': - resolution: {integrity: sha512-pAqOKUHeVWHEXXDIp/qoMk/6jyxIb6GGjnK1/f8dKHtKIEs4tKsnnL563gceEvdad53OPXIt86uoevCcCzmBnw==} - engines: {node: '>=16.0.0'} - - '@aws-sdk/util-endpoints@3.645.0': - resolution: {integrity: sha512-Oe+xaU4ic4PB1k3pb5VTC1/MWES13IlgpaQw01bVHGfwP6Yv6zZOxizRzca2Y3E+AyR+nKD7vXtHRY+w3bi4bg==} + '@aws-sdk/util-endpoints@3.693.0': + resolution: {integrity: sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==} engines: {node: '>=16.0.0'} '@aws-sdk/util-format-url@3.609.0': resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} engines: {node: '>=16.0.0'} + '@aws-sdk/util-format-url@3.693.0': + resolution: {integrity: sha512-0O4fSq45GOwC89Os0f92z9kK1AV22+W980O+v+GkMLUkRG7/nsIJkq1LKiIPV+sbC+KC/HmW4yThxFzHO7GDxA==} + engines: {node: '>=16.0.0'} + '@aws-sdk/util-locate-window@3.568.0': resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-user-agent-browser@3.387.0': - resolution: {integrity: sha512-lpgSVvDqx+JjHZCTYs/yQSS7J71dPlJeAlvxc7bmx5m+vfwKe07HAnIs+929DngS0QbAp/VaXbTiMFsInLkO4Q==} - '@aws-sdk/util-user-agent-browser@3.598.0': resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==} '@aws-sdk/util-user-agent-browser@3.609.0': resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} - '@aws-sdk/util-user-agent-node@3.387.0': - resolution: {integrity: sha512-r9OVkcWpRYatjLhJacuHFgvO2T5s/Nu5DDbScMrkUD8b4aGIIqsrdZji0vZy9FCjsUFQMM92t9nt4SejrGjChA==} - engines: {node: '>=14.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-browser@3.693.0': + resolution: {integrity: sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==} '@aws-sdk/util-user-agent-node@3.598.0': resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==} @@ -3718,8 +3467,8 @@ packages: aws-crt: optional: true - '@aws-sdk/util-user-agent-node@3.614.0': - resolution: {integrity: sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==} + '@aws-sdk/util-user-agent-node@3.693.0': + resolution: {integrity: sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==} engines: {node: '>=16.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -3730,12 +3479,8 @@ packages: '@aws-sdk/util-utf8-browser@3.259.0': resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} - '@aws-sdk/xml-builder@3.310.0': - resolution: {integrity: sha512-TqELu4mOuSIKQCqj63fGVs86Yh+vBx5nHRpWKNUNhB2nPTpfbziTs5c1X358be3peVWA4wPxW7Nt53KIg1tnNw==} - engines: {node: '>=14.0.0'} - - '@aws-sdk/xml-builder@3.598.0': - resolution: {integrity: sha512-ZIa2RK7CHFTZ4gwK77WRtsZ6vF7xwRXxJ8KQIxK2duhoTVcn0xYxpFLdW9WZZZvdP9GIF3Loqvf8DRdeU5Jc7Q==} + '@aws-sdk/xml-builder@3.693.0': + resolution: {integrity: sha512-C/rPwJcqnV8VDr2/VtcQnymSpcfEEgH1Jm6V0VmfXNZFv4Qzf1eCS8nsec0gipYgZB+cBBjfXw5dAk6pJ8ubpw==} engines: {node: '>=16.0.0'} '@babel/code-frame@7.24.7': @@ -4371,151 +4116,86 @@ packages: '@sinonjs/text-encoding@0.7.3': resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} - '@smithy/abort-controller@2.2.0': - resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} - engines: {node: '>=14.0.0'} - - '@smithy/abort-controller@3.1.1': - resolution: {integrity: sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==} - engines: {node: '>=16.0.0'} - '@smithy/abort-controller@3.1.5': resolution: {integrity: sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==} engines: {node: '>=16.0.0'} - '@smithy/chunked-blob-reader-native@2.2.0': - resolution: {integrity: sha512-VNB5+1oCgX3Fzs072yuRsUoC2N4Zg/LJ11DTxX3+Qu+Paa6AmbIF0E9sc2wthz9Psrk/zcOlTCyuposlIhPjZQ==} - - '@smithy/chunked-blob-reader-native@3.0.0': - resolution: {integrity: sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg==} - - '@smithy/chunked-blob-reader@2.2.0': - resolution: {integrity: sha512-3GJNvRwXBGdkDZZOGiziVYzDpn4j6zfyULHMDKAGIUo72yHALpE9CbhfQp/XcLNVoc1byfMpn6uW5H2BqPjgaQ==} - - '@smithy/chunked-blob-reader@3.0.0': - resolution: {integrity: sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA==} + '@smithy/abort-controller@3.1.8': + resolution: {integrity: sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==} + engines: {node: '>=16.0.0'} - '@smithy/config-resolver@2.2.0': - resolution: {integrity: sha512-fsiMgd8toyUba6n1WRmr+qACzXltpdDkPTAaDqc8QqPBUzO+/JKwL6bUBseHVi8tu9l+3JOK+tSf7cay+4B3LA==} - engines: {node: '>=14.0.0'} + '@smithy/chunked-blob-reader-native@3.0.1': + resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} - '@smithy/config-resolver@3.0.4': - resolution: {integrity: sha512-VwiOk7TwXoE7NlNguV/aPq1hFH72tqkHCw8eWXbr2xHspRyyv9DLpLXhq+Ieje+NwoqXrY0xyQjPXdOE6cGcHA==} - engines: {node: '>=16.0.0'} + '@smithy/chunked-blob-reader@4.0.0': + resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} - '@smithy/config-resolver@3.0.5': - resolution: {integrity: sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==} + '@smithy/config-resolver@3.0.12': + resolution: {integrity: sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==} engines: {node: '>=16.0.0'} '@smithy/config-resolver@3.0.9': resolution: {integrity: sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==} engines: {node: '>=16.0.0'} - '@smithy/core@2.2.4': - resolution: {integrity: sha512-qdY3LpMOUyLM/gfjjMQZui+UTNS7kBRDWlvyIhVOql5dn2J3isk9qUTBtQ1CbDH8MTugHis1zu3h4rH+Qmmh4g==} - engines: {node: '>=16.0.0'} - - '@smithy/core@2.4.0': - resolution: {integrity: sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==} - engines: {node: '>=16.0.0'} - '@smithy/core@2.4.8': resolution: {integrity: sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==} engines: {node: '>=16.0.0'} - '@smithy/credential-provider-imds@2.3.0': - resolution: {integrity: sha512-BWB9mIukO1wjEOo1Ojgl6LrG4avcaC7T/ZP6ptmAaW4xluhSIPZhY+/PI5YKzlk+jsm+4sQZB45Bt1OfMeQa3w==} - engines: {node: '>=14.0.0'} - - '@smithy/credential-provider-imds@3.2.0': - resolution: {integrity: sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==} + '@smithy/core@2.5.4': + resolution: {integrity: sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==} engines: {node: '>=16.0.0'} '@smithy/credential-provider-imds@3.2.4': resolution: {integrity: sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==} engines: {node: '>=16.0.0'} - '@smithy/eventstream-codec@2.2.0': - resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} - - '@smithy/eventstream-codec@3.1.2': - resolution: {integrity: sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==} - - '@smithy/eventstream-serde-browser@2.2.0': - resolution: {integrity: sha512-UaPf8jKbcP71BGiO0CdeLmlg+RhWnlN8ipsMSdwvqBFigl5nil3rHOI/5GE3tfiuX8LvY5Z9N0meuU7Rab7jWw==} - engines: {node: '>=14.0.0'} - - '@smithy/eventstream-serde-browser@3.0.4': - resolution: {integrity: sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA==} + '@smithy/credential-provider-imds@3.2.7': + resolution: {integrity: sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==} engines: {node: '>=16.0.0'} - '@smithy/eventstream-serde-config-resolver@2.2.0': - resolution: {integrity: sha512-RHhbTw/JW3+r8QQH7PrganjNCiuiEZmpi6fYUAetFfPLfZ6EkiA08uN3EFfcyKubXQxOwTeJRZSQmDDCdUshaA==} - engines: {node: '>=14.0.0'} + '@smithy/eventstream-codec@3.1.9': + resolution: {integrity: sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==} - '@smithy/eventstream-serde-config-resolver@3.0.3': - resolution: {integrity: sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==} + '@smithy/eventstream-serde-browser@3.0.13': + resolution: {integrity: sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==} engines: {node: '>=16.0.0'} - '@smithy/eventstream-serde-node@2.2.0': - resolution: {integrity: sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==} - engines: {node: '>=14.0.0'} - - '@smithy/eventstream-serde-node@3.0.4': - resolution: {integrity: sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg==} + '@smithy/eventstream-serde-config-resolver@3.0.10': + resolution: {integrity: sha512-K1M0x7P7qbBUKB0UWIL5KOcyi6zqV5mPJoL0/o01HPJr0CSq3A9FYuJC6e11EX6hR8QTIR++DBiGrYveOu6trw==} engines: {node: '>=16.0.0'} - '@smithy/eventstream-serde-universal@2.2.0': - resolution: {integrity: sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==} - engines: {node: '>=14.0.0'} - - '@smithy/eventstream-serde-universal@3.0.4': - resolution: {integrity: sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A==} + '@smithy/eventstream-serde-node@3.0.12': + resolution: {integrity: sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==} engines: {node: '>=16.0.0'} - '@smithy/fetch-http-handler@2.5.0': - resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} - - '@smithy/fetch-http-handler@3.2.0': - resolution: {integrity: sha512-vFvDxMrc6sO5Atec8PaISckMcAwsCrRhYxwUylg97bRT2KZoumOF7qk5+6EVUtuM1IG9AJV5aqXnHln9ZdXHpg==} - - '@smithy/fetch-http-handler@3.2.4': - resolution: {integrity: sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==} + '@smithy/eventstream-serde-universal@3.0.12': + resolution: {integrity: sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==} + engines: {node: '>=16.0.0'} '@smithy/fetch-http-handler@3.2.9': resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} - '@smithy/hash-blob-browser@2.2.0': - resolution: {integrity: sha512-SGPoVH8mdXBqrkVCJ1Hd1X7vh1zDXojNN1yZyZTZsCno99hVue9+IYzWDjq/EQDDXxmITB0gBmuyPh8oAZSTcg==} + '@smithy/fetch-http-handler@4.1.1': + resolution: {integrity: sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==} - '@smithy/hash-blob-browser@3.1.2': - resolution: {integrity: sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg==} - - '@smithy/hash-node@2.2.0': - resolution: {integrity: sha512-zLWaC/5aWpMrHKpoDF6nqpNtBhlAYKF/7+9yMN7GpdR8CzohnWfGtMznPybnwSS8saaXBMxIGwJqR4HmRp6b3g==} - engines: {node: '>=14.0.0'} + '@smithy/hash-blob-browser@3.1.9': + resolution: {integrity: sha512-wOu78omaUuW5DE+PVWXiRKWRZLecARyP3xcq5SmkXUw9+utgN8HnSnBfrjL2B/4ZxgqPjaAJQkC/+JHf1ITVaQ==} - '@smithy/hash-node@3.0.3': - resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} + '@smithy/hash-node@3.0.10': + resolution: {integrity: sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==} engines: {node: '>=16.0.0'} '@smithy/hash-node@3.0.7': resolution: {integrity: sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==} engines: {node: '>=16.0.0'} - '@smithy/hash-stream-node@2.2.0': - resolution: {integrity: sha512-aT+HCATOSRMGpPI7bi7NSsTNVZE/La9IaxLXWoVAYMxHT5hGO3ZOGEMZQg8A6nNL+pdFGtZQtND1eoY084HgHQ==} - engines: {node: '>=14.0.0'} - - '@smithy/hash-stream-node@3.1.2': - resolution: {integrity: sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g==} + '@smithy/hash-stream-node@3.1.9': + resolution: {integrity: sha512-3XfHBjSP3oDWxLmlxnt+F+FqXpL3WlXs+XXaB6bV9Wo8BBu87fK1dSEsyH7Z4ZHRmwZ4g9lFMdf08m9hoX1iRA==} engines: {node: '>=16.0.0'} - '@smithy/invalid-dependency@2.2.0': - resolution: {integrity: sha512-nEDASdbKFKPXN2O6lOlTgrEEOO9NHIeO+HVvZnkqc8h5U9g3BIhWsvzFo+UcUbliMHvKNPD/zVxDrkP1Sbgp8Q==} - - '@smithy/invalid-dependency@3.0.3': - resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.10': + resolution: {integrity: sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==} '@smithy/invalid-dependency@3.0.7': resolution: {integrity: sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==} @@ -4528,152 +4208,85 @@ packages: resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} engines: {node: '>=16.0.0'} - '@smithy/md5-js@2.2.0': - resolution: {integrity: sha512-M26XTtt9IIusVMOWEAhIvFIr9jYj4ISPPGJROqw6vXngO3IYJCnVVSMFn4Tx1rUTG5BiKJNg9u2nxmBiZC5IlQ==} - - '@smithy/md5-js@3.0.3': - resolution: {integrity: sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==} + '@smithy/md5-js@3.0.10': + resolution: {integrity: sha512-m3bv6dApflt3fS2Y1PyWPUtRP7iuBlvikEOGwu0HsCZ0vE7zcIX+dBoh3e+31/rddagw8nj92j0kJg2TfV+SJA==} - '@smithy/middleware-content-length@2.2.0': - resolution: {integrity: sha512-5bl2LG1Ah/7E5cMSC+q+h3IpVHMeOkG0yLRyQT1p2aMJkSrZG7RlXHPuAgb7EyaFeidKEnnd/fNaLLaKlHGzDQ==} - engines: {node: '>=14.0.0'} - - '@smithy/middleware-content-length@3.0.3': - resolution: {integrity: sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ==} - engines: {node: '>=16.0.0'} - - '@smithy/middleware-content-length@3.0.5': - resolution: {integrity: sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==} + '@smithy/middleware-content-length@3.0.12': + resolution: {integrity: sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==} engines: {node: '>=16.0.0'} '@smithy/middleware-content-length@3.0.9': resolution: {integrity: sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==} engines: {node: '>=16.0.0'} - '@smithy/middleware-endpoint@2.5.1': - resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} - engines: {node: '>=14.0.0'} - - '@smithy/middleware-endpoint@3.0.4': - resolution: {integrity: sha512-whUJMEPwl3ANIbXjBXZVdJNgfV2ZU8ayln7xUM47rXL2txuenI7jQ/VFFwCzy5lCmXScjp6zYtptW5Evud8e9g==} - engines: {node: '>=16.0.0'} - - '@smithy/middleware-endpoint@3.1.0': - resolution: {integrity: sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==} - engines: {node: '>=16.0.0'} - '@smithy/middleware-endpoint@3.1.4': resolution: {integrity: sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==} engines: {node: '>=16.0.0'} - '@smithy/middleware-retry@2.3.1': - resolution: {integrity: sha512-P2bGufFpFdYcWvqpyqqmalRtwFUNUA8vHjJR5iGqbfR6mp65qKOLcUd6lTr4S9Gn/enynSrSf3p3FVgVAf6bXA==} - engines: {node: '>=14.0.0'} - - '@smithy/middleware-retry@3.0.15': - resolution: {integrity: sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==} + '@smithy/middleware-endpoint@3.2.4': + resolution: {integrity: sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==} engines: {node: '>=16.0.0'} '@smithy/middleware-retry@3.0.23': resolution: {integrity: sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==} engines: {node: '>=16.0.0'} - '@smithy/middleware-retry@3.0.7': - resolution: {integrity: sha512-f5q7Y09G+2h5ivkSx5CHvlAT4qRR3jBFEsfXyQ9nFNiWQlr8c48blnu5cmbTQ+p1xmIO14UXzKoF8d7Tm0Gsjw==} + '@smithy/middleware-retry@3.0.28': + resolution: {integrity: sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==} engines: {node: '>=16.0.0'} - '@smithy/middleware-serde@2.3.0': - resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} - engines: {node: '>=14.0.0'} - - '@smithy/middleware-serde@3.0.3': - resolution: {integrity: sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==} + '@smithy/middleware-serde@3.0.10': + resolution: {integrity: sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==} engines: {node: '>=16.0.0'} '@smithy/middleware-serde@3.0.7': resolution: {integrity: sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==} engines: {node: '>=16.0.0'} - '@smithy/middleware-stack@2.2.0': - resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} - engines: {node: '>=14.0.0'} - - '@smithy/middleware-stack@3.0.3': - resolution: {integrity: sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==} + '@smithy/middleware-stack@3.0.10': + resolution: {integrity: sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==} engines: {node: '>=16.0.0'} '@smithy/middleware-stack@3.0.7': resolution: {integrity: sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==} engines: {node: '>=16.0.0'} - '@smithy/node-config-provider@2.3.0': - resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} - engines: {node: '>=14.0.0'} - - '@smithy/node-config-provider@3.1.3': - resolution: {integrity: sha512-rxdpAZczzholz6CYZxtqDu/aKTxATD5DAUDVj7HoEulq+pDSQVWzbg0btZDlxeFfa6bb2b5tUvgdX5+k8jUqcg==} - engines: {node: '>=16.0.0'} - - '@smithy/node-config-provider@3.1.4': - resolution: {integrity: sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==} + '@smithy/node-config-provider@3.1.11': + resolution: {integrity: sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==} engines: {node: '>=16.0.0'} '@smithy/node-config-provider@3.1.8': resolution: {integrity: sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==} engines: {node: '>=16.0.0'} - '@smithy/node-http-handler@2.5.0': - resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} - engines: {node: '>=14.0.0'} - - '@smithy/node-http-handler@3.1.1': - resolution: {integrity: sha512-L71NLyPeP450r2J/mfu1jMc//Z1YnqJt2eSNw7uhiItaONnBLDA68J5jgxq8+MBDsYnFwNAIc7dBG1ImiWBiwg==} - engines: {node: '>=16.0.0'} - - '@smithy/node-http-handler@3.1.4': - resolution: {integrity: sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==} - engines: {node: '>=16.0.0'} - '@smithy/node-http-handler@3.2.4': resolution: {integrity: sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==} engines: {node: '>=16.0.0'} - '@smithy/property-provider@2.2.0': - resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} - engines: {node: '>=14.0.0'} + '@smithy/node-http-handler@3.3.1': + resolution: {integrity: sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==} + engines: {node: '>=16.0.0'} - '@smithy/property-provider@3.1.3': - resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} + '@smithy/property-provider@3.1.10': + resolution: {integrity: sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==} engines: {node: '>=16.0.0'} '@smithy/property-provider@3.1.7': resolution: {integrity: sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==} engines: {node: '>=16.0.0'} - '@smithy/protocol-http@2.0.5': - resolution: {integrity: sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==} - engines: {node: '>=14.0.0'} - - '@smithy/protocol-http@3.3.0': - resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} - engines: {node: '>=14.0.0'} - - '@smithy/protocol-http@4.0.3': - resolution: {integrity: sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw==} + '@smithy/protocol-http@4.1.4': + resolution: {integrity: sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==} engines: {node: '>=16.0.0'} - '@smithy/protocol-http@4.1.0': - resolution: {integrity: sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==} + '@smithy/protocol-http@4.1.7': + resolution: {integrity: sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==} engines: {node: '>=16.0.0'} - '@smithy/protocol-http@4.1.4': - resolution: {integrity: sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==} + '@smithy/querystring-builder@3.0.10': + resolution: {integrity: sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==} engines: {node: '>=16.0.0'} - '@smithy/querystring-builder@2.2.0': - resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} - engines: {node: '>=14.0.0'} - '@smithy/querystring-builder@3.0.3': resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} engines: {node: '>=16.0.0'} @@ -4682,36 +4295,24 @@ packages: resolution: {integrity: sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==} engines: {node: '>=16.0.0'} - '@smithy/querystring-parser@2.2.0': - resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} - engines: {node: '>=14.0.0'} - - '@smithy/querystring-parser@3.0.3': - resolution: {integrity: sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==} + '@smithy/querystring-parser@3.0.10': + resolution: {integrity: sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==} engines: {node: '>=16.0.0'} '@smithy/querystring-parser@3.0.7': resolution: {integrity: sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==} engines: {node: '>=16.0.0'} - '@smithy/service-error-classification@2.1.5': - resolution: {integrity: sha512-uBDTIBBEdAQryvHdc5W8sS5YX7RQzF683XrHePVdFmAgKiMofU15FLSM0/HU03hKTnazdNRFa0YHS7+ArwoUSQ==} - engines: {node: '>=14.0.0'} - - '@smithy/service-error-classification@3.0.3': - resolution: {integrity: sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==} + '@smithy/service-error-classification@3.0.10': + resolution: {integrity: sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==} engines: {node: '>=16.0.0'} '@smithy/service-error-classification@3.0.7': resolution: {integrity: sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==} engines: {node: '>=16.0.0'} - '@smithy/shared-ini-file-loader@2.4.0': - resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} - engines: {node: '>=14.0.0'} - - '@smithy/shared-ini-file-loader@3.1.4': - resolution: {integrity: sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==} + '@smithy/shared-ini-file-loader@3.1.11': + resolution: {integrity: sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==} engines: {node: '>=16.0.0'} '@smithy/shared-ini-file-loader@3.1.8': @@ -4726,30 +4327,18 @@ packages: resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} engines: {node: '>=16.0.0'} - '@smithy/signature-v4@4.1.0': - resolution: {integrity: sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==} - engines: {node: '>=16.0.0'} - - '@smithy/signature-v4@4.2.0': - resolution: {integrity: sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ==} - engines: {node: '>=16.0.0'} - - '@smithy/smithy-client@2.5.1': - resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} - engines: {node: '>=14.0.0'} - - '@smithy/smithy-client@3.1.5': - resolution: {integrity: sha512-x9bL9Mx2CT2P1OiUlHM+ZNpbVU6TgT32f9CmTRzqIHA7M4vYrROCWEoC3o4xHNJASoGd4Opos3cXYPgh+/m4Ww==} - engines: {node: '>=16.0.0'} - - '@smithy/smithy-client@3.2.0': - resolution: {integrity: sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==} + '@smithy/signature-v4@4.2.3': + resolution: {integrity: sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==} engines: {node: '>=16.0.0'} '@smithy/smithy-client@3.4.0': resolution: {integrity: sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==} engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.4.5': + resolution: {integrity: sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==} + engines: {node: '>=16.0.0'} + '@smithy/types@2.12.0': resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} engines: {node: '>=14.0.0'} @@ -4758,33 +4347,23 @@ packages: resolution: {integrity: sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==} engines: {node: '>=16.0.0'} - '@smithy/url-parser@2.2.0': - resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} + '@smithy/types@3.7.1': + resolution: {integrity: sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==} + engines: {node: '>=16.0.0'} - '@smithy/url-parser@3.0.3': - resolution: {integrity: sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==} + '@smithy/url-parser@3.0.10': + resolution: {integrity: sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==} '@smithy/url-parser@3.0.7': resolution: {integrity: sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==} - '@smithy/util-base64@2.3.0': - resolution: {integrity: sha512-s3+eVwNeJuXUwuMbusncZNViuhv2LjVJ1nMwTqSA0XAC7gjKhqqxRdJPhR8+YrkoZ9IiIbFk/yK6ACe/xlF+hw==} - engines: {node: '>=14.0.0'} - '@smithy/util-base64@3.0.0': resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} engines: {node: '>=16.0.0'} - '@smithy/util-body-length-browser@2.2.0': - resolution: {integrity: sha512-dtpw9uQP7W+n3vOtx0CfBD5EWd7EPdIdsQnWTDoFf77e3VUf05uA7R7TGipIo8e4WL2kuPdnsr3hMQn9ziYj5w==} - '@smithy/util-body-length-browser@3.0.0': resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} - '@smithy/util-body-length-node@2.3.0': - resolution: {integrity: sha512-ITWT1Wqjubf2CJthb0BuT9+bpzBfXeMokH/AAa5EJQgbv9aPMVfnM76iFIZVFf50hYXGbtiV71BHAthNWd6+dw==} - engines: {node: '>=14.0.0'} - '@smithy/util-body-length-node@3.0.0': resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} engines: {node: '>=16.0.0'} @@ -4797,58 +4376,34 @@ packages: resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} engines: {node: '>=16.0.0'} - '@smithy/util-config-provider@2.3.0': - resolution: {integrity: sha512-HZkzrRcuFN1k70RLqlNK4FnPXKOpkik1+4JaBoHNJn+RnJGYqaa3c5/+XtLOXhlKzlRgNvyaLieHTW2VwGN0VQ==} - engines: {node: '>=14.0.0'} - '@smithy/util-config-provider@3.0.0': resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} engines: {node: '>=16.0.0'} - '@smithy/util-defaults-mode-browser@2.2.1': - resolution: {integrity: sha512-RtKW+8j8skk17SYowucwRUjeh4mCtnm5odCL0Lm2NtHQBsYKrNW0od9Rhopu9wF1gHMfHeWF7i90NwBz/U22Kw==} - engines: {node: '>= 10.0.0'} - - '@smithy/util-defaults-mode-browser@3.0.15': - resolution: {integrity: sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==} - engines: {node: '>= 10.0.0'} - '@smithy/util-defaults-mode-browser@3.0.23': resolution: {integrity: sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==} engines: {node: '>= 10.0.0'} - '@smithy/util-defaults-mode-browser@3.0.7': - resolution: {integrity: sha512-Q2txLyvQyGfmjsaDbVV7Sg8psefpFcrnlGapDzXGFRPFKRBeEg6OvFK8FljqjeHSaCZ6/UuzQExUPqBR/2qlDA==} - engines: {node: '>= 10.0.0'} - - '@smithy/util-defaults-mode-node@2.3.1': - resolution: {integrity: sha512-vkMXHQ0BcLFysBMWgSBLSk3+leMpFSyyFj8zQtv5ZyUBx8/owVh1/pPEkzmW/DR/Gy/5c8vjLDD9gZjXNKbrpA==} - engines: {node: '>= 10.0.0'} - - '@smithy/util-defaults-mode-node@3.0.15': - resolution: {integrity: sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==} + '@smithy/util-defaults-mode-browser@3.0.28': + resolution: {integrity: sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==} engines: {node: '>= 10.0.0'} '@smithy/util-defaults-mode-node@3.0.23': resolution: {integrity: sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==} engines: {node: '>= 10.0.0'} - '@smithy/util-defaults-mode-node@3.0.7': - resolution: {integrity: sha512-F4Qcj1fG6MGi2BSWCslfsMSwllws/WzYONBGtLybyY+halAcXdWhcew+mej8M5SKd5hqPYp4f7b+ABQEaeytgg==} + '@smithy/util-defaults-mode-node@3.0.28': + resolution: {integrity: sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==} engines: {node: '>= 10.0.0'} - '@smithy/util-endpoints@2.0.4': - resolution: {integrity: sha512-ZAtNf+vXAsgzgRutDDiklU09ZzZiiV/nATyqde4Um4priTmasDH+eLpp3tspL0hS2dEootyFMhu1Y6Y+tzpWBQ==} - engines: {node: '>=16.0.0'} - - '@smithy/util-endpoints@2.0.5': - resolution: {integrity: sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==} - engines: {node: '>=16.0.0'} - '@smithy/util-endpoints@2.1.3': resolution: {integrity: sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==} engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.1.6': + resolution: {integrity: sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==} + engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@2.2.0': resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} engines: {node: '>=14.0.0'} @@ -4861,38 +4416,30 @@ packages: resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} engines: {node: '>=14.0.0'} - '@smithy/util-middleware@3.0.3': - resolution: {integrity: sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==} + '@smithy/util-middleware@3.0.10': + resolution: {integrity: sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==} engines: {node: '>=16.0.0'} '@smithy/util-middleware@3.0.7': resolution: {integrity: sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==} engines: {node: '>=16.0.0'} - '@smithy/util-retry@2.2.0': - resolution: {integrity: sha512-q9+pAFPTfftHXRytmZ7GzLFFrEGavqapFc06XxzZFcSIGERXMerXxCitjOG1prVDR9QdjqotF40SWvbqcCpf8g==} - engines: {node: '>= 14.0.0'} - - '@smithy/util-retry@3.0.3': - resolution: {integrity: sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==} + '@smithy/util-retry@3.0.10': + resolution: {integrity: sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==} engines: {node: '>=16.0.0'} '@smithy/util-retry@3.0.7': resolution: {integrity: sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==} engines: {node: '>=16.0.0'} - '@smithy/util-stream@2.2.0': - resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} - engines: {node: '>=14.0.0'} - - '@smithy/util-stream@3.1.3': - resolution: {integrity: sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==} - engines: {node: '>=16.0.0'} - '@smithy/util-stream@3.1.9': resolution: {integrity: sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==} engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.3.1': + resolution: {integrity: sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==} + engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@2.2.0': resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} engines: {node: '>=14.0.0'} @@ -4909,12 +4456,8 @@ packages: resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} engines: {node: '>=16.0.0'} - '@smithy/util-waiter@2.2.0': - resolution: {integrity: sha512-IHk53BVw6MPMi2Gsn+hCng8rFA3ZmR3Rk7GllxDUW9qFJl/hiSvskn7XldkECapQVkIg/1dHpMAxI9xSTaLLSA==} - engines: {node: '>=14.0.0'} - - '@smithy/util-waiter@3.1.2': - resolution: {integrity: sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==} + '@smithy/util-waiter@3.1.9': + resolution: {integrity: sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==} engines: {node: '>=16.0.0'} '@testcontainers/postgresql@10.9.0': @@ -8032,64 +7575,27 @@ snapshots: call-me-maybe: 1.0.2 openapi-types: 12.1.3 - '@aws-crypto/crc32@3.0.0': - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.667.0 - tslib: 1.14.1 - '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.692.0 tslib: 2.6.3 - '@aws-crypto/crc32c@3.0.0': - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.667.0 - tslib: 1.14.1 - '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.692.0 tslib: 2.6.3 - '@aws-crypto/ie11-detection@3.0.0': - dependencies: - tslib: 1.14.1 - - '@aws-crypto/sha1-browser@3.0.0': - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.692.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - '@aws-crypto/sha256-browser@3.0.0': - dependencies: - '@aws-crypto/ie11-detection': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-crypto/supports-web-crypto': 3.0.0 - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-locate-window': 3.568.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - '@aws-crypto/sha256-browser@5.2.0': dependencies: '@aws-crypto/sha256-js': 5.2.0 @@ -8100,12 +7606,6 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - '@aws-crypto/sha256-js@3.0.0': - dependencies: - '@aws-crypto/util': 3.0.0 - '@aws-sdk/types': 3.667.0 - tslib: 1.14.1 - '@aws-crypto/sha256-js@4.0.0': dependencies: '@aws-crypto/util': 4.0.0 @@ -8115,23 +7615,13 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.667.0 + '@aws-sdk/types': 3.692.0 tslib: 2.6.3 - '@aws-crypto/supports-web-crypto@3.0.0': - dependencies: - tslib: 1.14.1 - '@aws-crypto/supports-web-crypto@5.2.0': dependencies: tslib: 2.6.3 - '@aws-crypto/util@3.0.0': - dependencies: - '@aws-sdk/types': 3.667.0 - '@aws-sdk/util-utf8-browser': 3.259.0 - tslib: 1.14.1 - '@aws-crypto/util@4.0.0': dependencies: '@aws-sdk/types': 3.667.0 @@ -8190,148 +7680,51 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-dynamodb@3.602.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-endpoint-discovery': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.2 - tslib: 2.6.3 - uuid: 9.0.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-dynamodb@3.637.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/client-sts': 3.637.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/middleware-endpoint-discovery': 3.620.0 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.637.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 - '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.2 - tslib: 2.6.3 - uuid: 9.0.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-dynamodb@3.648.0': + '@aws-sdk/client-dynamodb@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/middleware-endpoint-discovery': 3.620.0 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.645.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.3 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-endpoint-discovery': 3.693.0 + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.2 + '@smithy/util-waiter': 3.1.9 + '@types/uuid': 9.0.8 tslib: 2.6.3 uuid: 9.0.1 transitivePeerDependencies: @@ -8341,8 +7734,8 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) + '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) + '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8383,228 +7776,214 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-s3@3.387.0': - dependencies: - '@aws-crypto/sha1-browser': 3.0.0 - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.387.0 - '@aws-sdk/credential-provider-node': 3.387.0 - '@aws-sdk/middleware-bucket-endpoint': 3.387.0 - '@aws-sdk/middleware-expect-continue': 3.387.0 - '@aws-sdk/middleware-flexible-checksums': 3.387.0 - '@aws-sdk/middleware-host-header': 3.387.0 - '@aws-sdk/middleware-location-constraint': 3.387.0 - '@aws-sdk/middleware-logger': 3.387.0 - '@aws-sdk/middleware-recursion-detection': 3.387.0 - '@aws-sdk/middleware-sdk-s3': 3.387.0 - '@aws-sdk/middleware-signing': 3.387.0 - '@aws-sdk/middleware-ssec': 3.387.0 - '@aws-sdk/middleware-user-agent': 3.387.0 - '@aws-sdk/signature-v4-multi-region': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@aws-sdk/util-endpoints': 3.387.0 - '@aws-sdk/util-user-agent-browser': 3.387.0 - '@aws-sdk/util-user-agent-node': 3.387.0 - '@aws-sdk/xml-builder': 3.310.0 - '@smithy/config-resolver': 2.2.0 - '@smithy/eventstream-serde-browser': 2.2.0 - '@smithy/eventstream-serde-config-resolver': 2.2.0 - '@smithy/eventstream-serde-node': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-blob-browser': 2.2.0 - '@smithy/hash-node': 2.2.0 - '@smithy/hash-stream-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/md5-js': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-stream': 2.2.0 - '@smithy/util-utf8': 2.3.0 - '@smithy/util-waiter': 2.2.0 - fast-xml-parser: 4.2.5 + '@aws-sdk/client-kms@3.693.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - - '@aws-sdk/signature-v4-crt' - aws-crt - '@aws-sdk/client-s3@3.600.0': + '@aws-sdk/client-s3@3.693.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-bucket-endpoint': 3.598.0 - '@aws-sdk/middleware-expect-continue': 3.598.0 - '@aws-sdk/middleware-flexible-checksums': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-location-constraint': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-sdk-s3': 3.598.0 - '@aws-sdk/middleware-signing': 3.598.0 - '@aws-sdk/middleware-ssec': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/signature-v4-multi-region': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@aws-sdk/xml-builder': 3.598.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/eventstream-serde-browser': 3.0.4 - '@smithy/eventstream-serde-config-resolver': 3.0.3 - '@smithy/eventstream-serde-node': 3.0.4 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-blob-browser': 3.1.2 - '@smithy/hash-node': 3.0.3 - '@smithy/hash-stream-node': 3.1.2 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/md5-js': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.3 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-bucket-endpoint': 3.693.0 + '@aws-sdk/middleware-expect-continue': 3.693.0 + '@aws-sdk/middleware-flexible-checksums': 3.693.0 + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-location-constraint': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-sdk-s3': 3.693.0 + '@aws-sdk/middleware-ssec': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/signature-v4-multi-region': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@aws-sdk/xml-builder': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/eventstream-serde-browser': 3.0.13 + '@smithy/eventstream-serde-config-resolver': 3.0.10 + '@smithy/eventstream-serde-node': 3.0.12 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-blob-browser': 3.1.9 + '@smithy/hash-node': 3.0.10 + '@smithy/hash-stream-node': 3.1.9 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/md5-js': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-retry': 3.0.3 - '@smithy/util-stream': 3.1.3 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-stream': 3.3.1 '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.1.2 + '@smithy/util-waiter': 3.1.9 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sesv2@3.620.1': + '@aws-sdk/client-sesv2@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) - '@aws-sdk/client-sts': 3.620.1 - '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.620.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.5 - '@smithy/core': 2.4.0 - '@smithy/fetch-http-handler': 3.2.4 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.5 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/middleware-retry': 3.0.15 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.4 - '@smithy/node-http-handler': 3.1.4 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.3 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.15 - '@smithy/util-defaults-mode-node': 3.0.15 - '@smithy/util-endpoints': 2.0.5 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sqs@3.600.0': + '@aws-sdk/client-sqs@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-sdk-sqs': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.4 - '@smithy/core': 2.2.4 - '@smithy/fetch-http-handler': 3.2.0 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/md5-js': 3.0.3 - '@smithy/middleware-content-length': 3.0.3 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.7 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.1 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.5 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.3 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-sdk-sqs': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/md5-js': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.7 - '@smithy/util-defaults-mode-node': 3.0.7 - '@smithy/util-endpoints': 2.0.4 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.3 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.600.0': + '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) + '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 @@ -8643,6 +8022,7 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: + - '@aws-sdk/client-sts' - aws-crt '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': @@ -8690,67 +8070,65 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)': + '@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.620.1 - '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.620.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)': + '@aws-sdk/client-sso@3.598.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.637.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.637.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 + '@aws-sdk/core': 3.598.0 + '@aws-sdk/middleware-host-header': 3.598.0 + '@aws-sdk/middleware-logger': 3.598.0 + '@aws-sdk/middleware-recursion-detection': 3.598.0 + '@aws-sdk/middleware-user-agent': 3.598.0 + '@aws-sdk/region-config-resolver': 3.598.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 + '@aws-sdk/util-user-agent-browser': 3.598.0 + '@aws-sdk/util-user-agent-node': 3.598.0 '@smithy/config-resolver': 3.0.9 '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 @@ -8780,22 +8158,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)': + '@aws-sdk/client-sso@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/middleware-host-header': 3.620.0 + '@aws-sdk/core': 3.609.0 + '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.645.0 - '@aws-sdk/region-config-resolver': 3.614.0 + '@aws-sdk/middleware-recursion-detection': 3.609.0 + '@aws-sdk/middleware-user-agent': 3.609.0 + '@aws-sdk/region-config-resolver': 3.609.0 '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 + '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 + '@aws-sdk/util-user-agent-node': 3.609.0 '@smithy/config-resolver': 3.0.9 '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 @@ -8825,49 +8201,56 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.387.0': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/middleware-host-header': 3.387.0 - '@aws-sdk/middleware-logger': 3.387.0 - '@aws-sdk/middleware-recursion-detection': 3.387.0 - '@aws-sdk/middleware-user-agent': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@aws-sdk/util-endpoints': 3.387.0 - '@aws-sdk/util-user-agent-browser': 3.387.0 - '@aws-sdk/util-user-agent-node': 3.387.0 - '@smithy/config-resolver': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-utf8': 2.3.0 + '@aws-sdk/client-sso@3.693.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.598.0': + '@aws-sdk/client-sts@3.600.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/core': 3.598.0 + '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) '@aws-sdk/middleware-host-header': 3.598.0 '@aws-sdk/middleware-logger': 3.598.0 '@aws-sdk/middleware-recursion-detection': 3.598.0 @@ -8906,11 +8289,13 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.609.0': + '@aws-sdk/client-sts@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -8949,404 +8334,62 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.620.1': + '@aws-sdk/client-sts@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.620.1 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.620.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/middleware-host-header': 3.693.0 + '@aws-sdk/middleware-logger': 3.693.0 + '@aws-sdk/middleware-recursion-detection': 3.693.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/region-config-resolver': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@aws-sdk/util-user-agent-browser': 3.693.0 + '@aws-sdk/util-user-agent-node': 3.693.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/core': 2.5.4 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 + '@smithy/util-defaults-mode-browser': 3.0.28 + '@smithy/util-defaults-mode-node': 3.0.28 + '@smithy/util-endpoints': 2.1.6 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.637.0': + '@aws-sdk/core@3.598.0': dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.637.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 '@smithy/protocol-http': 4.1.4 + '@smithy/signature-v4': 3.1.2 '@smithy/smithy-client': 3.4.0 '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 + fast-xml-parser: 4.2.5 tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-sso@3.645.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.635.0 - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.645.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.387.0': - dependencies: - '@aws-crypto/sha256-browser': 3.0.0 - '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/credential-provider-node': 3.387.0 - '@aws-sdk/middleware-host-header': 3.387.0 - '@aws-sdk/middleware-logger': 3.387.0 - '@aws-sdk/middleware-recursion-detection': 3.387.0 - '@aws-sdk/middleware-sdk-sts': 3.387.0 - '@aws-sdk/middleware-signing': 3.387.0 - '@aws-sdk/middleware-user-agent': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@aws-sdk/util-endpoints': 3.387.0 - '@aws-sdk/util-user-agent-browser': 3.387.0 - '@aws-sdk/util-user-agent-node': 3.387.0 - '@smithy/config-resolver': 2.2.0 - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/hash-node': 2.2.0 - '@smithy/invalid-dependency': 2.2.0 - '@smithy/middleware-content-length': 2.2.0 - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-retry': 2.3.1 - '@smithy/middleware-serde': 2.3.0 - '@smithy/middleware-stack': 2.2.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-body-length-browser': 2.2.0 - '@smithy/util-body-length-node': 2.3.0 - '@smithy/util-defaults-mode-browser': 2.2.1 - '@smithy/util-defaults-mode-node': 2.3.1 - '@smithy/util-retry': 2.2.0 - '@smithy/util-utf8': 2.3.0 - fast-xml-parser: 4.2.5 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - - '@aws-sdk/client-sts@3.609.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/middleware-host-header': 3.609.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.609.0 - '@aws-sdk/middleware-user-agent': 3.609.0 - '@aws-sdk/region-config-resolver': 3.609.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.609.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.620.1': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) - '@aws-sdk/core': 3.620.1 - '@aws-sdk/credential-provider-node': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.620.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.637.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.637.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/client-sts@3.645.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/core': 3.635.0 - '@aws-sdk/credential-provider-node': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/middleware-host-header': 3.620.0 - '@aws-sdk/middleware-logger': 3.609.0 - '@aws-sdk/middleware-recursion-detection': 3.620.0 - '@aws-sdk/middleware-user-agent': 3.645.0 - '@aws-sdk/region-config-resolver': 3.614.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 - '@aws-sdk/util-user-agent-browser': 3.609.0 - '@aws-sdk/util-user-agent-node': 3.614.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/core@3.598.0': + '@aws-sdk/core@3.609.0': dependencies: '@smithy/core': 2.4.8 '@smithy/protocol-http': 4.1.4 @@ -9356,38 +8399,17 @@ snapshots: fast-xml-parser: 4.2.5 tslib: 2.6.3 - '@aws-sdk/core@3.609.0': - dependencies: - '@smithy/core': 2.4.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - fast-xml-parser: 4.2.5 - tslib: 2.6.3 - - '@aws-sdk/core@3.620.1': - dependencies: - '@smithy/core': 2.4.8 - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 4.2.0 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-middleware': 3.0.7 - fast-xml-parser: 4.2.5 - tslib: 2.6.3 - - '@aws-sdk/core@3.635.0': + '@aws-sdk/core@3.693.0': dependencies: - '@smithy/core': 2.4.8 - '@smithy/node-config-provider': 3.1.8 - '@smithy/property-provider': 3.1.3 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 4.1.0 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-middleware': 3.0.7 + '@aws-sdk/types': 3.692.0 + '@smithy/core': 2.5.4 + '@smithy/node-config-provider': 3.1.11 + '@smithy/property-provider': 3.1.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/signature-v4': 4.2.3 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-middleware': 3.0.10 fast-xml-parser: 4.4.1 tslib: 2.6.3 @@ -9401,13 +8423,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-env@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/property-provider': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-env@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 @@ -9422,11 +8437,12 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-env@3.620.1': + '@aws-sdk/credential-provider-env@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/types': 3.5.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/property-provider': 3.1.10 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.598.0': @@ -9453,48 +8469,22 @@ snapshots: '@smithy/util-stream': 3.1.9 tslib: 2.6.3 - '@aws-sdk/credential-provider-http@3.620.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/node-http-handler': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-stream': 3.1.9 - tslib: 2.6.3 - - '@aws-sdk/credential-provider-http@3.635.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/node-http-handler': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-stream': 3.1.9 - tslib: 2.6.3 - - '@aws-sdk/credential-provider-ini@3.387.0': + '@aws-sdk/credential-provider-http@3.693.0': dependencies: - '@aws-sdk/credential-provider-env': 3.387.0 - '@aws-sdk/credential-provider-process': 3.387.0 - '@aws-sdk/credential-provider-sso': 3.387.0 - '@aws-sdk/credential-provider-web-identity': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@smithy/credential-provider-imds': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/node-http-handler': 3.3.1 + '@smithy/property-provider': 3.1.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-stream': 3.3.1 tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) + '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/credential-provider-env': 3.598.0 '@aws-sdk/credential-provider-http': 3.598.0 '@aws-sdk/credential-provider-process': 3.598.0 @@ -9528,14 +8518,14 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-ini@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1)': + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0)': dependencies: - '@aws-sdk/client-sts': 3.620.1 - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.620.0 - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.4 '@smithy/property-provider': 3.1.7 @@ -9546,14 +8536,14 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-ini@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0)': + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: - '@aws-sdk/client-sts': 3.637.0 - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0 + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.4 '@smithy/property-provider': 3.1.7 @@ -9563,41 +8553,27 @@ snapshots: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt + optional: true - '@aws-sdk/credential-provider-ini@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)': - dependencies: - '@aws-sdk/client-sts': 3.645.0 - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 + '@aws-sdk/credential-provider-ini@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0)': + dependencies: + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/credential-provider-env': 3.693.0 + '@aws-sdk/credential-provider-http': 3.693.0 + '@aws-sdk/credential-provider-process': 3.693.0 + '@aws-sdk/credential-provider-sso': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/credential-provider-web-identity': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/types': 3.692.0 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-node@3.387.0': - dependencies: - '@aws-sdk/credential-provider-env': 3.387.0 - '@aws-sdk/credential-provider-ini': 3.387.0 - '@aws-sdk/credential-provider-process': 3.387.0 - '@aws-sdk/credential-provider-sso': 3.387.0 - '@aws-sdk/credential-provider-web-identity': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@smithy/credential-provider-imds': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': dependencies: '@aws-sdk/credential-provider-env': 3.598.0 @@ -9636,18 +8612,18 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-node@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1)': + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0)': dependencies: - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.620.0 - '@aws-sdk/credential-provider-ini': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))(@aws-sdk/client-sts@3.620.1) - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) - '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.620.1) + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: @@ -9655,14 +8631,14 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-node@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0)': + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.637.0) + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0 + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.4 '@smithy/property-provider': 3.1.7 @@ -9673,34 +8649,27 @@ snapshots: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt + optional: true - '@aws-sdk/credential-provider-node@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0)': + '@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0)': dependencies: - '@aws-sdk/credential-provider-env': 3.620.1 - '@aws-sdk/credential-provider-http': 3.635.0 - '@aws-sdk/credential-provider-ini': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/credential-provider-process': 3.620.1 - '@aws-sdk/credential-provider-sso': 3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) - '@aws-sdk/credential-provider-web-identity': 3.621.0(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 + '@aws-sdk/credential-provider-env': 3.693.0 + '@aws-sdk/credential-provider-http': 3.693.0 + '@aws-sdk/credential-provider-ini': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-process': 3.693.0 + '@aws-sdk/credential-provider-sso': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/credential-provider-web-identity': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/types': 3.692.0 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-process@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-process@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 @@ -9717,26 +8686,15 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-process@3.620.1': + '@aws-sdk/credential-provider-process@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/credential-provider-sso@3.387.0': - dependencies: - '@aws-sdk/client-sso': 3.387.0 - '@aws-sdk/token-providers': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: '@aws-sdk/client-sso': 3.598.0 @@ -9750,10 +8708,10 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': + '@aws-sdk/credential-provider-sso@3.609.0': dependencies: '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -9762,11 +8720,12 @@ snapshots: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt + optional: true - '@aws-sdk/credential-provider-sso@3.620.1(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': + '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: - '@aws-sdk/client-sso': 3.620.1 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1)) + '@aws-sdk/client-sso': 3.609.0 + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -9776,10 +8735,10 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-sso@3.637.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': + '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: - '@aws-sdk/client-sso': 3.637.0 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0)) + '@aws-sdk/client-sso': 3.609.0 + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -9789,29 +8748,23 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-sso@3.645.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': + '@aws-sdk/credential-provider-sso@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: - '@aws-sdk/client-sso': 3.645.0 - '@aws-sdk/token-providers': 3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0)) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 + '@aws-sdk/client-sso': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/token-providers': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/types': 3.692.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-web-identity@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/property-provider': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': dependencies: - '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0) + '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.7 '@smithy/types': 3.5.0 @@ -9825,31 +8778,39 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.620.1)': + '@aws-sdk/credential-provider-web-identity@3.693.0(@aws-sdk/client-sts@3.693.0)': dependencies: - '@aws-sdk/client-sts': 3.620.1 - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.637.0)': - dependencies: - '@aws-sdk/client-sts': 3.637.0 - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/types': 3.5.0 + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/property-provider': 3.1.10 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.621.0(@aws-sdk/client-sts@3.645.0)': + '@aws-sdk/credential-providers@3.609.0': dependencies: - '@aws-sdk/client-sts': 3.645.0 + '@aws-sdk/client-cognito-identity': 3.609.0 + '@aws-sdk/client-sso': 3.609.0 + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-cognito-identity': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0 + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.4 '@smithy/property-provider': 3.1.7 '@smithy/types': 3.5.0 tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + optional: true - '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': + '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/client-sso': 3.609.0 @@ -9857,10 +8818,10 @@ snapshots: '@aws-sdk/credential-provider-cognito-identity': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.4 @@ -9871,91 +8832,53 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/endpoint-cache@3.572.0': + '@aws-sdk/endpoint-cache@3.693.0': dependencies: mnemonist: 0.38.3 tslib: 2.6.3 - '@aws-sdk/middleware-bucket-endpoint@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@aws-sdk/util-arn-parser': 3.310.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - '@smithy/util-config-provider': 2.3.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-bucket-endpoint@3.598.0': + '@aws-sdk/middleware-bucket-endpoint@3.693.0': dependencies: - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-endpoint-discovery@3.598.0': - dependencies: - '@aws-sdk/endpoint-cache': 3.572.0 - '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-endpoint-discovery@3.620.0': - dependencies: - '@aws-sdk/endpoint-cache': 3.572.0 - '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-expect-continue@3.387.0': + '@aws-sdk/middleware-endpoint-discovery@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-expect-continue@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@aws-sdk/endpoint-cache': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-flexible-checksums@3.387.0': + '@aws-sdk/middleware-expect-continue@3.693.0': dependencies: - '@aws-crypto/crc32': 3.0.0 - '@aws-crypto/crc32c': 3.0.0 - '@aws-sdk/types': 3.387.0 - '@smithy/is-array-buffer': 2.2.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - '@smithy/util-utf8': 2.3.0 + '@aws-sdk/types': 3.692.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-flexible-checksums@3.598.0': + '@aws-sdk/middleware-flexible-checksums@3.693.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 - '@aws-sdk/types': 3.598.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-stream': 3.3.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-host-header@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - '@aws-sdk/middleware-host-header@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 @@ -9970,29 +8893,17 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/middleware-host-header@3.620.0': + '@aws-sdk/middleware-host-header@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@aws-sdk/types': 3.692.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-location-constraint@3.387.0': + '@aws-sdk/middleware-location-constraint@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-location-constraint@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-logger@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/types': 2.12.0 + '@aws-sdk/types': 3.692.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@aws-sdk/middleware-logger@3.598.0': @@ -10004,165 +8915,92 @@ snapshots: '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-recursion-detection@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-recursion-detection@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-recursion-detection@3.609.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-recursion-detection@3.620.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-sdk-s3@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 - '@aws-sdk/util-arn-parser': 3.310.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-sdk-s3@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-config-provider': 3.0.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-sdk-s3@3.622.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-arn-parser': 3.568.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 4.2.0 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-stream': 3.1.9 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-sdk-sqs@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - - '@aws-sdk/middleware-sdk-sts@3.387.0': - dependencies: - '@aws-sdk/middleware-signing': 3.387.0 - '@aws-sdk/types': 3.387.0 - '@smithy/types': 2.12.0 + '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/middleware-signing@3.387.0': + '@aws-sdk/middleware-logger@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/property-provider': 2.2.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/signature-v4': 2.3.0 - '@smithy/types': 2.12.0 - '@smithy/util-middleware': 2.2.0 + '@aws-sdk/types': 3.692.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-signing@3.598.0': + '@aws-sdk/middleware-recursion-detection@3.598.0': dependencies: '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.3 '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 3.1.2 '@smithy/types': 3.5.0 - '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 - '@aws-sdk/middleware-ssec@3.387.0': + '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/types': 2.12.0 + '@aws-sdk/types': 3.609.0 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/middleware-ssec@3.598.0': + '@aws-sdk/middleware-recursion-detection@3.693.0': dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.5.0 + '@aws-sdk/types': 3.692.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.387.0': + '@aws-sdk/middleware-sdk-s3@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@aws-sdk/util-endpoints': 3.387.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/types': 2.12.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-arn-parser': 3.693.0 + '@smithy/core': 2.5.4 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/signature-v4': 4.2.3 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-stream': 3.3.1 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.598.0': + '@aws-sdk/middleware-sdk-sqs@3.693.0': dependencies: - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@aws-sdk/types': 3.692.0 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.609.0': + '@aws-sdk/middleware-ssec@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.609.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@aws-sdk/types': 3.692.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.620.0': + '@aws-sdk/middleware-user-agent@3.598.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.614.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 '@smithy/protocol-http': 4.1.4 '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.637.0': + '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.637.0 + '@aws-sdk/util-endpoints': 3.609.0 '@smithy/protocol-http': 4.1.4 '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.645.0': + '@aws-sdk/middleware-user-agent@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-endpoints': 3.645.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@aws-sdk/core': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-endpoints': 3.693.0 + '@smithy/core': 2.5.4 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.598.0': @@ -10183,63 +9021,38 @@ snapshots: '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 - '@aws-sdk/region-config-resolver@3.614.0': + '@aws-sdk/region-config-resolver@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 + '@aws-sdk/types': 3.692.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.7 - tslib: 2.6.3 - - '@aws-sdk/s3-request-presigner@3.623.0': - dependencies: - '@aws-sdk/signature-v4-multi-region': 3.622.0 - '@aws-sdk/types': 3.609.0 - '@aws-sdk/util-format-url': 3.609.0 - '@smithy/middleware-endpoint': 3.1.0 - '@smithy/protocol-http': 4.1.0 - '@smithy/smithy-client': 3.2.0 - '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 - '@aws-sdk/signature-v4-multi-region@3.387.0': + '@aws-sdk/s3-request-presigner@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/protocol-http': 2.0.5 - '@smithy/signature-v4': 2.3.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@aws-sdk/signature-v4-multi-region@3.598.0': - dependencies: - '@aws-sdk/middleware-sdk-s3': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 3.1.2 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/signature-v4-multi-region@3.622.0': - dependencies: - '@aws-sdk/middleware-sdk-s3': 3.622.0 - '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 4.2.0 - '@smithy/types': 3.5.0 + '@aws-sdk/signature-v4-multi-region': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@aws-sdk/util-format-url': 3.693.0 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/token-providers@3.387.0': + '@aws-sdk/signature-v4-multi-region@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 + '@aws-sdk/middleware-sdk-s3': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/signature-v4': 4.2.3 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) '@aws-sdk/types': 3.598.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -10255,36 +9068,22 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.620.1(@aws-sdk/client-sts@3.620.1))': + '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.620.1(@aws-sdk/client-sts@3.620.1) + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.637.0(@aws-sdk/client-sts@3.637.0))': + '@aws-sdk/token-providers@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.637.0(@aws-sdk/client-sts@3.637.0) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/token-providers@3.614.0(@aws-sdk/client-sso-oidc@3.645.0(@aws-sdk/client-sts@3.645.0))': - dependencies: - '@aws-sdk/client-sso-oidc': 3.645.0(@aws-sdk/client-sts@3.645.0) - '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@aws-sdk/types@3.387.0': - dependencies: - '@smithy/types': 2.12.0 + '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/types': 3.692.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@aws-sdk/types@3.598.0': @@ -10302,37 +9101,18 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/util-arn-parser@3.310.0': - dependencies: - tslib: 2.6.3 - - '@aws-sdk/util-arn-parser@3.568.0': - dependencies: - tslib: 2.6.3 - - '@aws-sdk/util-dynamodb@3.602.0(@aws-sdk/client-dynamodb@3.602.0)': - dependencies: - '@aws-sdk/client-dynamodb': 3.602.0 - tslib: 2.6.3 - - '@aws-sdk/util-dynamodb@3.637.0(@aws-sdk/client-dynamodb@3.637.0)': + '@aws-sdk/types@3.692.0': dependencies: - '@aws-sdk/client-dynamodb': 3.637.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/util-dynamodb@3.648.0(@aws-sdk/client-dynamodb@3.648.0)': + '@aws-sdk/util-arn-parser@3.693.0': dependencies: - '@aws-sdk/client-dynamodb': 3.648.0 tslib: 2.6.3 - '@aws-sdk/util-dynamodb@3.658.1(@aws-sdk/client-dynamodb@3.637.0)': + '@aws-sdk/util-dynamodb@3.693.0(@aws-sdk/client-dynamodb@3.693.0)': dependencies: - '@aws-sdk/client-dynamodb': 3.637.0 - tslib: 2.6.3 - - '@aws-sdk/util-endpoints@3.387.0': - dependencies: - '@aws-sdk/types': 3.387.0 + '@aws-sdk/client-dynamodb': 3.693.0 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.598.0': @@ -10349,25 +9129,11 @@ snapshots: '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 - '@aws-sdk/util-endpoints@3.614.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.5.0 - '@smithy/util-endpoints': 2.1.3 - tslib: 2.6.3 - - '@aws-sdk/util-endpoints@3.637.0': - dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.5.0 - '@smithy/util-endpoints': 2.1.3 - tslib: 2.6.3 - - '@aws-sdk/util-endpoints@3.645.0': + '@aws-sdk/util-endpoints@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.5.0 - '@smithy/util-endpoints': 2.1.3 + '@aws-sdk/types': 3.692.0 + '@smithy/types': 3.7.1 + '@smithy/util-endpoints': 2.1.6 tslib: 2.6.3 '@aws-sdk/util-format-url@3.609.0': @@ -10377,15 +9143,15 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/util-locate-window@3.568.0': + '@aws-sdk/util-format-url@3.693.0': dependencies: + '@aws-sdk/types': 3.692.0 + '@smithy/querystring-builder': 3.0.10 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/util-user-agent-browser@3.387.0': + '@aws-sdk/util-locate-window@3.568.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/types': 2.12.0 - bowser: 2.11.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-browser@3.598.0': @@ -10402,11 +9168,11 @@ snapshots: bowser: 2.11.0 tslib: 2.6.3 - '@aws-sdk/util-user-agent-node@3.387.0': + '@aws-sdk/util-user-agent-browser@3.693.0': dependencies: - '@aws-sdk/types': 3.387.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/types': 2.12.0 + '@aws-sdk/types': 3.692.0 + '@smithy/types': 3.7.1 + bowser: 2.11.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-node@3.598.0': @@ -10423,24 +9189,21 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@aws-sdk/util-user-agent-node@3.614.0': + '@aws-sdk/util-user-agent-node@3.693.0': dependencies: - '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 + '@aws-sdk/middleware-user-agent': 3.693.0 + '@aws-sdk/types': 3.692.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@aws-sdk/util-utf8-browser@3.259.0': dependencies: tslib: 2.6.3 - '@aws-sdk/xml-builder@3.310.0': - dependencies: - tslib: 2.6.3 - - '@aws-sdk/xml-builder@3.598.0': + '@aws-sdk/xml-builder@3.693.0': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@babel/code-frame@7.24.7': @@ -10997,61 +9760,31 @@ snapshots: '@sinonjs/text-encoding@0.7.3': {} - '@smithy/abort-controller@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/abort-controller@3.1.1': - dependencies: - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@smithy/abort-controller@3.1.5': dependencies: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/chunked-blob-reader-native@2.2.0': + '@smithy/abort-controller@3.1.8': dependencies: - '@smithy/util-base64': 2.3.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/chunked-blob-reader-native@3.0.0': + '@smithy/chunked-blob-reader-native@3.0.1': dependencies: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - '@smithy/chunked-blob-reader@2.2.0': - dependencies: - tslib: 2.6.3 - - '@smithy/chunked-blob-reader@3.0.0': - dependencies: - tslib: 2.6.3 - - '@smithy/config-resolver@2.2.0': - dependencies: - '@smithy/node-config-provider': 2.3.0 - '@smithy/types': 2.12.0 - '@smithy/util-config-provider': 2.3.0 - '@smithy/util-middleware': 2.2.0 - tslib: 2.6.3 - - '@smithy/config-resolver@3.0.4': + '@smithy/chunked-blob-reader@4.0.0': dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 - '@smithy/config-resolver@3.0.5': + '@smithy/config-resolver@3.0.12': dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.7 + '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 '@smithy/config-resolver@3.0.9': @@ -11062,18 +9795,7 @@ snapshots: '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 - '@smithy/core@2.2.4': - dependencies: - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-middleware': 3.0.7 - tslib: 2.6.3 - - '@smithy/core@2.4.0': + '@smithy/core@2.4.8': dependencies: '@smithy/middleware-endpoint': 3.1.4 '@smithy/middleware-retry': 3.0.23 @@ -11086,35 +9808,17 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/core@2.4.8': + '@smithy/core@2.5.4': dependencies: - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 + '@smithy/middleware-serde': 3.0.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-middleware': 3.0.7 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-stream': 3.3.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/credential-provider-imds@2.3.0': - dependencies: - '@smithy/node-config-provider': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - tslib: 2.6.3 - - '@smithy/credential-provider-imds@3.2.0': - dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/property-provider': 3.1.7 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - tslib: 2.6.3 - '@smithy/credential-provider-imds@3.2.4': dependencies: '@smithy/node-config-provider': 3.1.8 @@ -11123,232 +9827,121 @@ snapshots: '@smithy/url-parser': 3.0.7 tslib: 2.6.3 - '@smithy/eventstream-codec@2.2.0': + '@smithy/credential-provider-imds@3.2.7': dependencies: - '@aws-crypto/crc32': 3.0.0 - '@smithy/types': 2.12.0 - '@smithy/util-hex-encoding': 2.2.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/property-provider': 3.1.10 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 tslib: 2.6.3 - '@smithy/eventstream-codec@3.1.2': + '@smithy/eventstream-codec@3.1.9': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 '@smithy/util-hex-encoding': 3.0.0 tslib: 2.6.3 - '@smithy/eventstream-serde-browser@2.2.0': - dependencies: - '@smithy/eventstream-serde-universal': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-browser@3.0.4': - dependencies: - '@smithy/eventstream-serde-universal': 3.0.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-config-resolver@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-config-resolver@3.0.3': - dependencies: - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-node@2.2.0': - dependencies: - '@smithy/eventstream-serde-universal': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-node@3.0.4': - dependencies: - '@smithy/eventstream-serde-universal': 3.0.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-universal@2.2.0': - dependencies: - '@smithy/eventstream-codec': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/eventstream-serde-universal@3.0.4': - dependencies: - '@smithy/eventstream-codec': 3.1.2 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/fetch-http-handler@2.5.0': - dependencies: - '@smithy/protocol-http': 3.3.0 - '@smithy/querystring-builder': 2.2.0 - '@smithy/types': 2.12.0 - '@smithy/util-base64': 2.3.0 - tslib: 2.6.3 - - '@smithy/fetch-http-handler@3.2.0': - dependencies: - '@smithy/protocol-http': 4.1.4 - '@smithy/querystring-builder': 3.0.7 - '@smithy/types': 3.5.0 - '@smithy/util-base64': 3.0.0 - tslib: 2.6.3 - - '@smithy/fetch-http-handler@3.2.4': - dependencies: - '@smithy/protocol-http': 4.1.4 - '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.5.0 - '@smithy/util-base64': 3.0.0 - tslib: 2.6.3 - - '@smithy/fetch-http-handler@3.2.9': - dependencies: - '@smithy/protocol-http': 4.1.4 - '@smithy/querystring-builder': 3.0.7 - '@smithy/types': 3.5.0 - '@smithy/util-base64': 3.0.0 - tslib: 2.6.3 - - '@smithy/hash-blob-browser@2.2.0': - dependencies: - '@smithy/chunked-blob-reader': 2.2.0 - '@smithy/chunked-blob-reader-native': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/hash-blob-browser@3.1.2': - dependencies: - '@smithy/chunked-blob-reader': 3.0.0 - '@smithy/chunked-blob-reader-native': 3.0.0 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/hash-node@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - '@smithy/util-buffer-from': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - - '@smithy/hash-node@3.0.3': - dependencies: - '@smithy/types': 3.5.0 - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - - '@smithy/hash-node@3.0.7': + '@smithy/eventstream-serde-browser@3.0.13': dependencies: - '@smithy/types': 3.5.0 - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/eventstream-serde-universal': 3.0.12 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/hash-stream-node@2.2.0': + '@smithy/eventstream-serde-config-resolver@3.0.10': dependencies: - '@smithy/types': 2.12.0 - '@smithy/util-utf8': 2.3.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/hash-stream-node@3.1.2': + '@smithy/eventstream-serde-node@3.0.12': dependencies: - '@smithy/types': 3.5.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/eventstream-serde-universal': 3.0.12 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/invalid-dependency@2.2.0': + '@smithy/eventstream-serde-universal@3.0.12': dependencies: - '@smithy/types': 2.12.0 + '@smithy/eventstream-codec': 3.1.9 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/invalid-dependency@3.0.3': + '@smithy/fetch-http-handler@3.2.9': dependencies: + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 '@smithy/types': 3.5.0 + '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - '@smithy/invalid-dependency@3.0.7': + '@smithy/fetch-http-handler@4.1.1': dependencies: - '@smithy/types': 3.5.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/querystring-builder': 3.0.10 + '@smithy/types': 3.7.1 + '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - '@smithy/is-array-buffer@2.2.0': + '@smithy/hash-blob-browser@3.1.9': dependencies: + '@smithy/chunked-blob-reader': 4.0.0 + '@smithy/chunked-blob-reader-native': 3.0.1 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/is-array-buffer@3.0.0': + '@smithy/hash-node@3.0.10': dependencies: + '@smithy/types': 3.7.1 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/md5-js@2.2.0': + '@smithy/hash-node@3.0.7': dependencies: - '@smithy/types': 2.12.0 - '@smithy/util-utf8': 2.3.0 + '@smithy/types': 3.5.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/md5-js@3.0.3': + '@smithy/hash-stream-node@3.1.9': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/middleware-content-length@2.2.0': + '@smithy/invalid-dependency@3.0.10': dependencies: - '@smithy/protocol-http': 3.3.0 - '@smithy/types': 2.12.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/middleware-content-length@3.0.3': + '@smithy/invalid-dependency@3.0.7': dependencies: - '@smithy/protocol-http': 4.1.4 '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/middleware-content-length@3.0.5': + '@smithy/is-array-buffer@2.2.0': dependencies: - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/middleware-content-length@3.0.9': + '@smithy/is-array-buffer@3.0.0': dependencies: - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/middleware-endpoint@2.5.1': + '@smithy/md5-js@3.0.10': dependencies: - '@smithy/middleware-serde': 2.3.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - '@smithy/url-parser': 2.2.0 - '@smithy/util-middleware': 2.2.0 + '@smithy/types': 3.7.1 + '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/middleware-endpoint@3.0.4': + '@smithy/middleware-content-length@3.0.12': dependencies: - '@smithy/middleware-serde': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-middleware': 3.0.7 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/middleware-endpoint@3.1.0': + '@smithy/middleware-content-length@3.0.9': dependencies: - '@smithy/middleware-serde': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/shared-ini-file-loader': 3.1.4 + '@smithy/protocol-http': 4.1.4 '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@smithy/middleware-endpoint@3.1.4': @@ -11361,29 +9954,16 @@ snapshots: '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 - '@smithy/middleware-retry@2.3.1': - dependencies: - '@smithy/node-config-provider': 2.3.0 - '@smithy/protocol-http': 3.3.0 - '@smithy/service-error-classification': 2.1.5 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - '@smithy/util-middleware': 2.2.0 - '@smithy/util-retry': 2.2.0 - tslib: 2.6.3 - uuid: 9.0.1 - - '@smithy/middleware-retry@3.0.15': + '@smithy/middleware-endpoint@3.2.4': dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/service-error-classification': 3.0.3 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 + '@smithy/core': 2.5.4 + '@smithy/middleware-serde': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 + '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 - uuid: 9.0.1 '@smithy/middleware-retry@3.0.23': dependencies: @@ -11397,26 +9977,21 @@ snapshots: tslib: 2.6.3 uuid: 9.0.1 - '@smithy/middleware-retry@3.0.7': + '@smithy/middleware-retry@3.0.28': dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/service-error-classification': 3.0.7 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 + '@smithy/node-config-provider': 3.1.11 + '@smithy/protocol-http': 4.1.7 + '@smithy/service-error-classification': 3.0.10 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/util-middleware': 3.0.10 + '@smithy/util-retry': 3.0.10 tslib: 2.6.3 uuid: 9.0.1 - '@smithy/middleware-serde@2.3.0': + '@smithy/middleware-serde@3.0.10': dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/middleware-serde@3.0.3': - dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/middleware-serde@3.0.7': @@ -11424,14 +9999,9 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/middleware-stack@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/middleware-stack@3.0.3': + '@smithy/middleware-stack@3.0.10': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/middleware-stack@3.0.7': @@ -11439,25 +10009,11 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/node-config-provider@2.3.0': - dependencies: - '@smithy/property-provider': 2.2.0 - '@smithy/shared-ini-file-loader': 2.4.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/node-config-provider@3.1.3': - dependencies: - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/node-config-provider@3.1.4': + '@smithy/node-config-provider@3.1.11': dependencies: - '@smithy/property-provider': 3.1.3 - '@smithy/shared-ini-file-loader': 3.1.4 - '@smithy/types': 3.5.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/node-config-provider@3.1.8': @@ -11467,30 +10023,6 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/node-http-handler@2.5.0': - dependencies: - '@smithy/abort-controller': 2.2.0 - '@smithy/protocol-http': 3.3.0 - '@smithy/querystring-builder': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/node-http-handler@3.1.1': - dependencies: - '@smithy/abort-controller': 3.1.5 - '@smithy/protocol-http': 4.1.4 - '@smithy/querystring-builder': 3.0.7 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/node-http-handler@3.1.4': - dependencies: - '@smithy/abort-controller': 3.1.1 - '@smithy/protocol-http': 4.1.4 - '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@smithy/node-http-handler@3.2.4': dependencies: '@smithy/abort-controller': 3.1.5 @@ -11499,14 +10031,17 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/property-provider@2.2.0': + '@smithy/node-http-handler@3.3.1': dependencies: - '@smithy/types': 2.12.0 + '@smithy/abort-controller': 3.1.8 + '@smithy/protocol-http': 4.1.7 + '@smithy/querystring-builder': 3.0.10 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/property-provider@3.1.3': + '@smithy/property-provider@3.1.10': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/property-provider@3.1.7': @@ -11514,35 +10049,20 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/protocol-http@2.0.5': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/protocol-http@3.3.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/protocol-http@4.0.3': - dependencies: - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/protocol-http@4.1.0': + '@smithy/protocol-http@4.1.4': dependencies: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/protocol-http@4.1.4': + '@smithy/protocol-http@4.1.7': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/querystring-builder@2.2.0': + '@smithy/querystring-builder@3.0.10': dependencies: - '@smithy/types': 2.12.0 - '@smithy/util-uri-escape': 2.2.0 + '@smithy/types': 3.7.1 + '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 '@smithy/querystring-builder@3.0.3': @@ -11557,14 +10077,9 @@ snapshots: '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 - '@smithy/querystring-parser@2.2.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/querystring-parser@3.0.3': + '@smithy/querystring-parser@3.0.10': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/querystring-parser@3.0.7': @@ -11572,26 +10087,17 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/service-error-classification@2.1.5': - dependencies: - '@smithy/types': 2.12.0 - - '@smithy/service-error-classification@3.0.3': + '@smithy/service-error-classification@3.0.10': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 '@smithy/service-error-classification@3.0.7': dependencies: '@smithy/types': 3.5.0 - '@smithy/shared-ini-file-loader@2.4.0': - dependencies: - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/shared-ini-file-loader@3.1.4': + '@smithy/shared-ini-file-loader@3.1.11': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/shared-ini-file-loader@3.1.8': @@ -11619,38 +10125,18 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/signature-v4@4.1.0': + '@smithy/signature-v4@4.2.3': dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-uri-escape': 3.0.0 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - - '@smithy/signature-v4@4.2.0': - dependencies: - '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.7 + '@smithy/util-middleware': 3.0.10 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/smithy-client@2.5.1': - dependencies: - '@smithy/middleware-endpoint': 2.5.1 - '@smithy/middleware-stack': 2.2.0 - '@smithy/protocol-http': 3.3.0 - '@smithy/types': 2.12.0 - '@smithy/util-stream': 2.2.0 - tslib: 2.6.3 - - '@smithy/smithy-client@3.1.5': + '@smithy/smithy-client@3.4.0': dependencies: '@smithy/middleware-endpoint': 3.1.4 '@smithy/middleware-stack': 3.0.7 @@ -11659,22 +10145,14 @@ snapshots: '@smithy/util-stream': 3.1.9 tslib: 2.6.3 - '@smithy/smithy-client@3.2.0': - dependencies: - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-stack': 3.0.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - '@smithy/util-stream': 3.1.3 - tslib: 2.6.3 - - '@smithy/smithy-client@3.4.0': + '@smithy/smithy-client@3.4.5': dependencies: - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-stack': 3.0.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - '@smithy/util-stream': 3.1.9 + '@smithy/core': 2.5.4 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-stack': 3.0.10 + '@smithy/protocol-http': 4.1.7 + '@smithy/types': 3.7.1 + '@smithy/util-stream': 3.3.1 tslib: 2.6.3 '@smithy/types@2.12.0': @@ -11685,16 +10163,14 @@ snapshots: dependencies: tslib: 2.6.3 - '@smithy/url-parser@2.2.0': + '@smithy/types@3.7.1': dependencies: - '@smithy/querystring-parser': 2.2.0 - '@smithy/types': 2.12.0 tslib: 2.6.3 - '@smithy/url-parser@3.0.3': + '@smithy/url-parser@3.0.10': dependencies: - '@smithy/querystring-parser': 3.0.3 - '@smithy/types': 3.5.0 + '@smithy/querystring-parser': 3.0.10 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/url-parser@3.0.7': @@ -11703,30 +10179,16 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/util-base64@2.3.0': - dependencies: - '@smithy/util-buffer-from': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - '@smithy/util-base64@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/util-body-length-browser@2.2.0': - dependencies: - tslib: 2.6.3 - '@smithy/util-body-length-browser@3.0.0': dependencies: tslib: 2.6.3 - '@smithy/util-body-length-node@2.3.0': - dependencies: - tslib: 2.6.3 - '@smithy/util-body-length-node@3.0.0': dependencies: tslib: 2.6.3 @@ -11741,30 +10203,10 @@ snapshots: '@smithy/is-array-buffer': 3.0.0 tslib: 2.6.3 - '@smithy/util-config-provider@2.3.0': - dependencies: - tslib: 2.6.3 - '@smithy/util-config-provider@3.0.0': dependencies: tslib: 2.6.3 - '@smithy/util-defaults-mode-browser@2.2.1': - dependencies: - '@smithy/property-provider': 2.2.0 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - bowser: 2.11.0 - tslib: 2.6.3 - - '@smithy/util-defaults-mode-browser@3.0.15': - dependencies: - '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - bowser: 2.11.0 - tslib: 2.6.3 - '@smithy/util-defaults-mode-browser@3.0.23': dependencies: '@smithy/property-provider': 3.1.7 @@ -11773,34 +10215,14 @@ snapshots: bowser: 2.11.0 tslib: 2.6.3 - '@smithy/util-defaults-mode-browser@3.0.7': + '@smithy/util-defaults-mode-browser@3.0.28': dependencies: - '@smithy/property-provider': 3.1.7 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 + '@smithy/property-provider': 3.1.10 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 bowser: 2.11.0 tslib: 2.6.3 - '@smithy/util-defaults-mode-node@2.3.1': - dependencies: - '@smithy/config-resolver': 2.2.0 - '@smithy/credential-provider-imds': 2.3.0 - '@smithy/node-config-provider': 2.3.0 - '@smithy/property-provider': 2.2.0 - '@smithy/smithy-client': 2.5.1 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/util-defaults-mode-node@3.0.15': - dependencies: - '@smithy/config-resolver': 3.0.9 - '@smithy/credential-provider-imds': 3.2.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/property-provider': 3.1.3 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@smithy/util-defaults-mode-node@3.0.23': dependencies: '@smithy/config-resolver': 3.0.9 @@ -11811,32 +10233,26 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/util-defaults-mode-node@3.0.7': - dependencies: - '@smithy/config-resolver': 3.0.9 - '@smithy/credential-provider-imds': 3.2.4 - '@smithy/node-config-provider': 3.1.8 - '@smithy/property-provider': 3.1.7 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - - '@smithy/util-endpoints@2.0.4': + '@smithy/util-defaults-mode-node@3.0.28': dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 + '@smithy/config-resolver': 3.0.12 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/node-config-provider': 3.1.11 + '@smithy/property-provider': 3.1.10 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/util-endpoints@2.0.5': + '@smithy/util-endpoints@2.1.3': dependencies: '@smithy/node-config-provider': 3.1.8 '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/util-endpoints@2.1.3': + '@smithy/util-endpoints@2.1.6': dependencies: - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 + '@smithy/node-config-provider': 3.1.11 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/util-hex-encoding@2.2.0': @@ -11852,9 +10268,9 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.6.3 - '@smithy/util-middleware@3.0.3': + '@smithy/util-middleware@3.0.10': dependencies: - '@smithy/types': 3.5.0 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/util-middleware@3.0.7': @@ -11862,16 +10278,10 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/util-retry@2.2.0': + '@smithy/util-retry@3.0.10': dependencies: - '@smithy/service-error-classification': 2.1.5 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/util-retry@3.0.3': - dependencies: - '@smithy/service-error-classification': 3.0.3 - '@smithy/types': 3.5.0 + '@smithy/service-error-classification': 3.0.10 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@smithy/util-retry@3.0.7': @@ -11880,18 +10290,7 @@ snapshots: '@smithy/types': 3.5.0 tslib: 2.6.3 - '@smithy/util-stream@2.2.0': - dependencies: - '@smithy/fetch-http-handler': 2.5.0 - '@smithy/node-http-handler': 2.5.0 - '@smithy/types': 2.12.0 - '@smithy/util-base64': 2.3.0 - '@smithy/util-buffer-from': 2.2.0 - '@smithy/util-hex-encoding': 2.2.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.6.3 - - '@smithy/util-stream@3.1.3': + '@smithy/util-stream@3.1.9': dependencies: '@smithy/fetch-http-handler': 3.2.9 '@smithy/node-http-handler': 3.2.4 @@ -11902,11 +10301,11 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/util-stream@3.1.9': + '@smithy/util-stream@3.3.1': dependencies: - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/node-http-handler': 3.2.4 - '@smithy/types': 3.5.0 + '@smithy/fetch-http-handler': 4.1.1 + '@smithy/node-http-handler': 3.3.1 + '@smithy/types': 3.7.1 '@smithy/util-base64': 3.0.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-hex-encoding': 3.0.0 @@ -11931,16 +10330,10 @@ snapshots: '@smithy/util-buffer-from': 3.0.0 tslib: 2.6.3 - '@smithy/util-waiter@2.2.0': - dependencies: - '@smithy/abort-controller': 2.2.0 - '@smithy/types': 2.12.0 - tslib: 2.6.3 - - '@smithy/util-waiter@3.1.2': + '@smithy/util-waiter@3.1.9': dependencies: - '@smithy/abort-controller': 3.1.5 - '@smithy/types': 3.5.0 + '@smithy/abort-controller': 3.1.8 + '@smithy/types': 3.7.1 tslib: 2.6.3 '@testcontainers/postgresql@10.9.0': @@ -12444,11 +10837,11 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 - aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)): + aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)): dependencies: '@aws-crypto/sha256-js': 4.0.0 - '@aws-sdk/client-sts': 3.609.0 - '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + '@aws-sdk/client-sts': 3.693.0 + '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/util-format-url': 3.609.0 '@smithy/signature-v4': 2.3.0 '@types/buffers': 0.1.31 @@ -14177,7 +12570,7 @@ snapshots: bson: 6.8.0 mongodb-connection-string-url: 3.0.1 optionalDependencies: - '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) + '@aws-sdk/credential-providers': 3.609.0 socks: 2.8.3 ms@2.0.0: {} From 10a440b10c1fb2ee7c2ebcc3827e6ee4369a490d Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 22 Nov 2024 17:52:13 +0100 Subject: [PATCH 036/126] Fix missing deps --- packages/authorization-server/package.json | 2 +- .../package.json | 5 +- pnpm-lock.yaml | 452 +----------------- 3 files changed, 14 insertions(+), 445 deletions(-) diff --git a/packages/authorization-server/package.json b/packages/authorization-server/package.json index aee65c4608..c53fb12315 100644 --- a/packages/authorization-server/package.json +++ b/packages/authorization-server/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "@aws-sdk/client-dynamodb": "3.693.0", - "@aws-sdk/client-kms": "3.600.0", + "@aws-sdk/client-kms": "3.693.0", "@aws-sdk/util-dynamodb": "3.693.0", "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", diff --git a/packages/ivass-certified-attributes-importer/package.json b/packages/ivass-certified-attributes-importer/package.json index abbc0c00f4..a83475c574 100644 --- a/packages/ivass-certified-attributes-importer/package.json +++ b/packages/ivass-certified-attributes-importer/package.json @@ -20,10 +20,10 @@ "license": "Apache-2.0", "devDependencies": { "@pagopa/eslint-config": "3.0.0", + "@types/adm-zip": "0.5.5", "@types/node": "20.14.6", "@types/ssh2-sftp-client": "9.0.4", "pagopa-interop-commons-test": "workspace:*", - "@types/adm-zip": "0.5.5", "prettier": "2.8.8", "testcontainers": "10.9.0", "tsx": "4.19.1", @@ -36,9 +36,10 @@ "axios": "1.7.4", "csv": "6.3.2", "dotenv-flow": "4.1.0", + "pagopa-interop-api-clients": "workspace:*", "pagopa-interop-commons": "workspace:*", "pagopa-interop-models": "workspace:*", - "pagopa-interop-api-clients": "workspace:*", + "ts-pattern": "5.2.0", "zod": "3.23.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc6faaff7e..3db304d8f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -814,8 +814,8 @@ importers: specifier: 3.693.0 version: 3.693.0 '@aws-sdk/client-kms': - specifier: 3.600.0 - version: 3.600.0 + specifier: 3.693.0 + version: 3.693.0 '@aws-sdk/util-dynamodb': specifier: 3.693.0 version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) @@ -2067,6 +2067,9 @@ importers: pagopa-interop-models: specifier: workspace:* version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -3092,10 +3095,6 @@ packages: resolution: {integrity: sha512-EmgFoE/wAxiOq/sfO/VFGlmvfq0FexUO4IMURr3deIpU/AuCsuU87HJH/UodFdKu88ykNZxMfHHku6o6BV2dAA==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-kms@3.600.0': - resolution: {integrity: sha512-m1o8aiVrVjExw6O+8JszXV3hr8sCyXKOLq1WCwWJqYF6Uf4vCf8iTYISQB3skbKUnBJm4SxVA82iViGAtWB7JA==} - engines: {node: '>=16.0.0'} - '@aws-sdk/client-kms@3.693.0': resolution: {integrity: sha512-9APrRDbScYqfCG89Trruf9+Bx39wlXQdwPEhluycpCF+bHYiTKdIXc4uix6hjbXzK0NKbgitus8bbMvsHX+Oxg==} engines: {node: '>=16.0.0'} @@ -3112,10 +3111,6 @@ packages: resolution: {integrity: sha512-Nfmo6bgad26PP1B7MZP9kpxOw874cVEvJHXBXwkSLg6ZPAeU2cnvTEE++omHkzLeeB9MbpmvhQtdaUn4c7DYZQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sso-oidc@3.600.0': - resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} - engines: {node: '>=16.0.0'} - '@aws-sdk/client-sso-oidc@3.609.0': resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} engines: {node: '>=16.0.0'} @@ -3128,10 +3123,6 @@ packages: peerDependencies: '@aws-sdk/client-sts': ^3.693.0 - '@aws-sdk/client-sso@3.598.0': - resolution: {integrity: sha512-nOI5lqPYa+YZlrrzwAJywJSw3MKVjvu6Ge2fCqQUNYMfxFB0NAaDFnl0EPjXi+sEbtCuz/uWE77poHbqiZ+7Iw==} - engines: {node: '>=16.0.0'} - '@aws-sdk/client-sso@3.609.0': resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} engines: {node: '>=16.0.0'} @@ -3140,10 +3131,6 @@ packages: resolution: {integrity: sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==} engines: {node: '>=16.0.0'} - '@aws-sdk/client-sts@3.600.0': - resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} - engines: {node: '>=16.0.0'} - '@aws-sdk/client-sts@3.609.0': resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} engines: {node: '>=16.0.0'} @@ -3152,10 +3139,6 @@ packages: resolution: {integrity: sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/core@3.598.0': - resolution: {integrity: sha512-HaSjt7puO5Cc7cOlrXFCW0rtA0BM9lvzjl56x0A20Pt+0wxXGeTOZZOkXQIepbrFkV2e/HYukuT9e99vXDm59g==} - engines: {node: '>=16.0.0'} - '@aws-sdk/core@3.609.0': resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} engines: {node: '>=16.0.0'} @@ -3168,10 +3151,6 @@ packages: resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-env@3.598.0': - resolution: {integrity: sha512-vi1khgn7yXzLCcgSIzQrrtd2ilUM0dWodxj3PQ6BLfP0O+q1imO3hG1nq7DVyJtq7rFHs6+9N8G4mYvTkxby2w==} - engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-env@3.609.0': resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} engines: {node: '>=16.0.0'} @@ -3180,10 +3159,6 @@ packages: resolution: {integrity: sha512-hMUZaRSF7+iBKZfBHNLihFs9zvpM1CB8MBOTnTp5NGCVkRYF3SB2LH+Kcippe0ats4qCyB1eEoyQX99rERp2iQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-http@3.598.0': - resolution: {integrity: sha512-N7cIafi4HVlQvEgvZSo1G4T9qb/JMLGMdBsDCT5XkeJrF0aptQWzTFH0jIdZcLrMYvzPcuEyO3yCBe6cy/ba0g==} - engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-http@3.609.0': resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} engines: {node: '>=16.0.0'} @@ -3192,12 +3167,6 @@ packages: resolution: {integrity: sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-ini@3.598.0': - resolution: {integrity: sha512-/ppcIVUbRwDIwJDoYfp90X3+AuJo2mvE52Y1t2VSrvUovYn6N4v95/vXj6LS8CNDhz2jvEJYmu+0cTMHdhI6eA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 - '@aws-sdk/credential-provider-ini@3.609.0': resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} engines: {node: '>=16.0.0'} @@ -3210,10 +3179,6 @@ packages: peerDependencies: '@aws-sdk/client-sts': ^3.693.0 - '@aws-sdk/credential-provider-node@3.600.0': - resolution: {integrity: sha512-1pC7MPMYD45J7yFjA90SxpR0yaSvy+yZiq23aXhAPZLYgJBAxHLu0s0mDCk/piWGPh8+UGur5K0bVdx4B1D5hw==} - engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-node@3.609.0': resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} engines: {node: '>=16.0.0'} @@ -3222,10 +3187,6 @@ packages: resolution: {integrity: sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-process@3.598.0': - resolution: {integrity: sha512-rM707XbLW8huMk722AgjVyxu2tMZee++fNA8TJVNgs1Ma02Wx6bBrfIvlyK0rCcIRb0WdQYP6fe3Xhiu4e8IBA==} - engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-process@3.609.0': resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} engines: {node: '>=16.0.0'} @@ -3234,10 +3195,6 @@ packages: resolution: {integrity: sha512-cvxQkrTWHHjeHrPlj7EWXPnFSq8x7vMx+Zn1oTsMpCY445N9KuzjfJTkmNGwU2GT6rSZI9/0MM02aQvl5bBBTQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-sso@3.598.0': - resolution: {integrity: sha512-5InwUmrAuqQdOOgxTccRayMMkSmekdLk6s+az9tmikq0QFAHUCtofI+/fllMXSR9iL6JbGYi1940+EUmS4pHJA==} - engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-sso@3.609.0': resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} engines: {node: '>=16.0.0'} @@ -3246,12 +3203,6 @@ packages: resolution: {integrity: sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/credential-provider-web-identity@3.598.0': - resolution: {integrity: sha512-GV5GdiMbz5Tz9JO4NJtRoFXjW0GPEujA0j+5J/B723rTN+REHthJu48HdBKouHGhdzkDWkkh1bu52V02Wprw8w==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.598.0 - '@aws-sdk/credential-provider-web-identity@3.609.0': resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} engines: {node: '>=16.0.0'} @@ -3288,10 +3239,6 @@ packages: resolution: {integrity: sha512-xkS6zjuE11ob93H9t65kHzphXcUMnN2SmIm2wycUPg+hi8Q6DJA6U2p//6oXkrr9oHy1QvwtllRd7SAd63sFKQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-host-header@3.598.0': - resolution: {integrity: sha512-WiaG059YBQwQraNejLIi0gMNkX7dfPZ8hDIhvMr5aVPRbaHH8AYF3iNSsXYCHvA2Cfa1O9haYXsuMF9flXnCmA==} - engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-host-header@3.609.0': resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} engines: {node: '>=16.0.0'} @@ -3304,10 +3251,6 @@ packages: resolution: {integrity: sha512-eDAExTZ9uNIP7vs2JCVCOuWJauGueisBSn+Ovt7UvvuEUp6KOIJqn8oFxWmyUQu2GvbG4OcaTLgbqD95YHTB0Q==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-logger@3.598.0': - resolution: {integrity: sha512-bxBjf/VYiu3zfu8SYM2S9dQQc3tz5uBAOcPz/Bt8DyyK3GgOpjhschH/2XuUErsoUO1gDJqZSdGOmuHGZQn00Q==} - engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-logger@3.609.0': resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} engines: {node: '>=16.0.0'} @@ -3316,10 +3259,6 @@ packages: resolution: {integrity: sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-recursion-detection@3.598.0': - resolution: {integrity: sha512-vjT9BeFY9FeN0f8hm2l6F53tI0N5bUq6RcDkQXKNabXBnQxKptJRad6oP2X5y3FoVfBLOuDkQgiC2940GIPxtQ==} - engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-recursion-detection@3.609.0': resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} engines: {node: '>=16.0.0'} @@ -3340,10 +3279,6 @@ packages: resolution: {integrity: sha512-Ro5vzI7SRgEeuoMk3fKqFjGv6mG4c7VsSCDwnkiasmafQFBTPvUIpgmu2FXMHqW/OthvoiOzpSrlJ9Bwlx2f8A==} engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-user-agent@3.598.0': - resolution: {integrity: sha512-4tjESlHG5B5MdjUaLK7tQs/miUtHbb6deauQx8ryqSBYOhfHVgb1ZnzvQR0bTrhpqUg0WlybSkDaZAICf9xctg==} - engines: {node: '>=16.0.0'} - '@aws-sdk/middleware-user-agent@3.609.0': resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} engines: {node: '>=16.0.0'} @@ -3352,10 +3287,6 @@ packages: resolution: {integrity: sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==} engines: {node: '>=16.0.0'} - '@aws-sdk/region-config-resolver@3.598.0': - resolution: {integrity: sha512-oYXhmTokSav4ytmWleCr3rs/1nyvZW/S0tdi6X7u+dLNL5Jee+uMxWGzgOrWK6wrQOzucLVjS4E/wA11Kv2GTw==} - engines: {node: '>=16.0.0'} - '@aws-sdk/region-config-resolver@3.609.0': resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} engines: {node: '>=16.0.0'} @@ -3372,12 +3303,6 @@ packages: resolution: {integrity: sha512-s7zbbsoVIriTR4ZGaateKuTqz6ddpazAyHvjk7I9kd+NvGNPiuAI18UdbuiiRI6K5HuYKf1ah6mKWFGPG15/kQ==} engines: {node: '>=16.0.0'} - '@aws-sdk/token-providers@3.598.0': - resolution: {integrity: sha512-TKY1EVdHVBnZqpyxyTHdpZpa1tUpb6nxVeRNn1zWG8QB5MvH4ALLd/jR+gtmWDNQbIG4cVuBOZFVL8hIYicKTA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.598.0 - '@aws-sdk/token-providers@3.609.0': resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} engines: {node: '>=16.0.0'} @@ -3390,10 +3315,6 @@ packages: peerDependencies: '@aws-sdk/client-sso-oidc': ^3.693.0 - '@aws-sdk/types@3.598.0': - resolution: {integrity: sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ==} - engines: {node: '>=16.0.0'} - '@aws-sdk/types@3.609.0': resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} engines: {node: '>=16.0.0'} @@ -3416,10 +3337,6 @@ packages: peerDependencies: '@aws-sdk/client-dynamodb': ^3.693.0 - '@aws-sdk/util-endpoints@3.598.0': - resolution: {integrity: sha512-Qo9UoiVVZxcOEdiOMZg3xb1mzkTxrhd4qSlg5QQrfWPJVx/QOg+Iy0NtGxPtHtVZNHZxohYwDwV/tfsnDSE2gQ==} - engines: {node: '>=16.0.0'} - '@aws-sdk/util-endpoints@3.609.0': resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} engines: {node: '>=16.0.0'} @@ -3440,24 +3357,12 @@ packages: resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} engines: {node: '>=16.0.0'} - '@aws-sdk/util-user-agent-browser@3.598.0': - resolution: {integrity: sha512-36Sxo6F+ykElaL1mWzWjlg+1epMpSe8obwhCN1yGE7Js9ywy5U6k6l+A3q3YM9YRbm740sNxncbwLklMvuhTKw==} - '@aws-sdk/util-user-agent-browser@3.609.0': resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} '@aws-sdk/util-user-agent-browser@3.693.0': resolution: {integrity: sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==} - '@aws-sdk/util-user-agent-node@3.598.0': - resolution: {integrity: sha512-oyWGcOlfTdzkC6SVplyr0AGh54IMrDxbhg5RxJ5P+V4BKfcDoDcZV9xenUk9NsOi9MuUjxMumb9UJGkDhM1m0A==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true - '@aws-sdk/util-user-agent-node@3.609.0': resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} engines: {node: '>=16.0.0'} @@ -5507,6 +5412,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@3.0.0-alpha-1: @@ -7730,52 +7636,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-kms@3.600.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-kms@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -7979,52 +7839,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.600.0(@aws-sdk/client-sts@3.600.0)': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sts' - - aws-crt - '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -8115,49 +7929,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.598.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-sso@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -8244,51 +8015,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.600.0': - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.9 - '@smithy/core': 2.4.8 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.23 - '@smithy/util-defaults-mode-node': 3.0.23 - '@smithy/util-endpoints': 2.1.3 - '@smithy/util-middleware': 3.0.7 - '@smithy/util-retry': 3.0.7 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - aws-crt - '@aws-sdk/client-sts@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 @@ -8379,16 +8105,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.598.0': - dependencies: - '@smithy/core': 2.4.8 - '@smithy/protocol-http': 4.1.4 - '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - fast-xml-parser: 4.2.5 - tslib: 2.6.3 - '@aws-sdk/core@3.609.0': dependencies: '@smithy/core': 2.4.8 @@ -8423,13 +8139,6 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-env@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.7 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -8445,18 +8154,6 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/credential-provider-http@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/fetch-http-handler': 3.2.9 - '@smithy/node-http-handler': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/util-stream': 3.1.9 - tslib: 2.6.3 - '@aws-sdk/credential-provider-http@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -8482,24 +8179,6 @@ snapshots: '@smithy/util-stream': 3.3.1 tslib: 2.6.3 - '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': - dependencies: - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/credential-provider-env': 3.598.0 - '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-process': 3.598.0 - '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 @@ -8574,25 +8253,6 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)': - dependencies: - '@aws-sdk/credential-provider-env': 3.598.0 - '@aws-sdk/credential-provider-http': 3.598.0 - '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/credential-provider-process': 3.598.0 - '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/credential-provider-imds': 3.2.4 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - '@aws-sdk/client-sts' - - aws-crt - '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 @@ -8670,14 +8330,6 @@ snapshots: - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-process@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -8695,23 +8347,10 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/credential-provider-sso@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': - dependencies: - '@aws-sdk/client-sso': 3.598.0 - '@aws-sdk/token-providers': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0) - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.7 - '@smithy/shared-ini-file-loader': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - '@aws-sdk/credential-provider-sso@3.609.0': dependencies: '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/token-providers': 3.609.0 '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 @@ -8762,14 +8401,6 @@ snapshots: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-web-identity@3.598.0(@aws-sdk/client-sts@3.600.0)': - dependencies: - '@aws-sdk/client-sts': 3.600.0 - '@aws-sdk/types': 3.598.0 - '@smithy/property-provider': 3.1.7 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 @@ -8879,13 +8510,6 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-host-header@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -8906,12 +8530,6 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-logger@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -8924,13 +8542,6 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-recursion-detection@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -8977,14 +8588,6 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@smithy/protocol-http': 4.1.4 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -9003,15 +8606,6 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/region-config-resolver@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.7 - tslib: 2.6.3 - '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -9050,14 +8644,14 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/token-providers@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)': + '@aws-sdk/token-providers@3.609.0': dependencies: - '@aws-sdk/client-sso-oidc': 3.600.0(@aws-sdk/client-sts@3.600.0) - '@aws-sdk/types': 3.598.0 + '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.7 '@smithy/shared-ini-file-loader': 3.1.8 '@smithy/types': 3.5.0 tslib: 2.6.3 + optional: true '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: @@ -9086,11 +8680,6 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@aws-sdk/types@3.598.0': - dependencies: - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/types@3.609.0': dependencies: '@smithy/types': 3.5.0 @@ -9115,13 +8704,6 @@ snapshots: '@aws-sdk/client-dynamodb': 3.693.0 tslib: 2.6.3 - '@aws-sdk/util-endpoints@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.5.0 - '@smithy/util-endpoints': 2.1.3 - tslib: 2.6.3 - '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -9154,13 +8736,6 @@ snapshots: dependencies: tslib: 2.6.3 - '@aws-sdk/util-user-agent-browser@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/types': 3.5.0 - bowser: 2.11.0 - tslib: 2.6.3 - '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 @@ -9175,13 +8750,6 @@ snapshots: bowser: 2.11.0 tslib: 2.6.3 - '@aws-sdk/util-user-agent-node@3.598.0': - dependencies: - '@aws-sdk/types': 3.598.0 - '@smithy/node-config-provider': 3.1.8 - '@smithy/types': 3.5.0 - tslib: 2.6.3 - '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 From a9b4c142e1316eb5e52f2841b6dbe34fa0ab17a2 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Mon, 25 Nov 2024 12:29:30 +0100 Subject: [PATCH 037/126] Removing leftover uuids and string correlationIds after new services merge (#1215) --- packages/authorization-server/package.json | 2 -- .../src/services/tokenService.ts | 5 +++-- .../test/authorizationServer.integration.test.ts | 4 ++-- pnpm-lock.yaml | 12 ------------ 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/packages/authorization-server/package.json b/packages/authorization-server/package.json index c53fb12315..de5d9cc688 100644 --- a/packages/authorization-server/package.json +++ b/packages/authorization-server/package.json @@ -22,13 +22,11 @@ "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", - "@types/uuid": "9.0.8", "pagopa-interop-commons-test": "workspace:*", "prettier": "2.8.8", "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0", - "uuid": "10.0.0", "jose": "5.9.4" }, "dependencies": { diff --git a/packages/authorization-server/src/services/tokenService.ts b/packages/authorization-server/src/services/tokenService.ts index e582016a03..bb04a024e1 100644 --- a/packages/authorization-server/src/services/tokenService.ts +++ b/packages/authorization-server/src/services/tokenService.ts @@ -26,6 +26,7 @@ import { GSIPKEServiceIdDescriptorId, ClientAssertion, FullTokenGenerationStatesClientPurposeEntry, + CorrelationId, } from "pagopa-interop-models"; import { DynamoDBClient, @@ -91,7 +92,7 @@ export function tokenServiceBuilder({ return { async generateToken( request: authorizationServerApi.AccessTokenRequest, - correlationId: string, + correlationId: CorrelationId, logger: Logger ): Promise { const { errors: parametersErrors } = validateRequestParameters({ @@ -299,7 +300,7 @@ export const publishAudit = async ({ generatedToken: InteropConsumerToken | InteropApiToken; key: FullTokenGenerationStatesClientPurposeEntry; clientAssertion: ClientAssertion; - correlationId: string; + correlationId: CorrelationId; fileManager: FileManager; logger: Logger; }): Promise => { diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts index 158b500d10..bae5e1ccf7 100644 --- a/packages/authorization-server/test/authorizationServer.integration.test.ts +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -554,7 +554,7 @@ describe("authorization server tests", () => { const correlationId = generateId(); const response = await tokenService.generateToken( request, - correlationId, + unsafeBrandId(correlationId), genericLogger ); @@ -684,7 +684,7 @@ describe("authorization server tests", () => { const correlationId = generateId(); const result = await tokenService.generateToken( request, - correlationId, + unsafeBrandId(correlationId), genericLogger ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3db304d8f5..c4cc300720 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -874,9 +874,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 jose: specifier: 5.9.4 version: 5.9.4 @@ -892,9 +889,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -7245,10 +7239,6 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -13260,8 +13250,6 @@ snapshots: utils-merge@1.0.1: {} - uuid@10.0.0: {} - uuid@9.0.1: {} vary@1.1.2: {} From 7678da42688b89fc5dd8d459b34720d0a3ab170b Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Mon, 25 Nov 2024 12:46:15 +0100 Subject: [PATCH 038/126] Delegation process approval + revoke + PDF contract fixes (#1217) --- .../api-clients/open-api/delegationApi.yml | 6 + packages/delegation-process/package.json | 3 +- .../src/model/domain/errors.ts | 70 ++-- .../src/model/domain/models.ts | 38 +++ .../src/model/domain/toEvent.ts | 9 +- .../templates/delegationApprovedTemplate.html | 7 +- .../templates/delegationRevokedTemplate.html | 5 +- .../src/services/delegationContractBuilder.ts | 50 ++- .../src/services/delegationProducerService.ts | 89 ++--- .../src/services/validators.ts | 59 ++-- .../src/utilities/errorMappers.ts | 10 +- .../test/approveProducerDelegation.test.ts | 169 +++++----- .../test/createProducerDelegation.test.ts | 10 +- .../test/rejectProducerDelegation.test.ts | 49 +-- .../test/revokeProducerDelegation.test.ts | 309 ++++++------------ packages/delegation-process/test/utils.ts | 18 - pnpm-lock.yaml | 32 -- 17 files changed, 436 insertions(+), 497 deletions(-) diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml index d912ab74c0..409dfeb45c 100644 --- a/packages/api-clients/open-api/delegationApi.yml +++ b/packages/api-clients/open-api/delegationApi.yml @@ -201,6 +201,12 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + "409": + description: Conflict + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /producer/delegations/{delegationId}/approve: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" diff --git a/packages/delegation-process/package.json b/packages/delegation-process/package.json index 37e1b03a01..8b9da97bae 100644 --- a/packages/delegation-process/package.json +++ b/packages/delegation-process/package.json @@ -33,8 +33,7 @@ "tsx": "4.19.1", "typescript": "5.4.5", "vitest": "1.6.0", - "puppeteer": "22.11.2", - "pdf-lib": "1.17.1" + "puppeteer": "22.11.2" }, "dependencies": { "@zodios/core": "10.9.6", diff --git a/packages/delegation-process/src/model/domain/errors.ts b/packages/delegation-process/src/model/domain/errors.ts index 7822e1e03c..72a24fd4a7 100644 --- a/packages/delegation-process/src/model/domain/errors.ts +++ b/packages/delegation-process/src/model/domain/errors.ts @@ -7,6 +7,7 @@ import { DelegationState, DelegationKind, Tenant, + DelegationId, } from "pagopa-interop-models"; import { match } from "ts-pattern"; @@ -18,8 +19,8 @@ export const errorCodes = { invalidDelegatorAndDelegateIds: "0005", tenantIsNotIPAError: "0006", tenantNotAllowedToDelegation: "0007", - delegationNotRevokable: "0008", - operationNotAllowOnDelegation: "0009", + stampNotFound: "0008", + operationRestrictedToDelegator: "0009", operationRestrictedToDelegate: "0010", incorrectState: "0011", differentEserviceProducer: "0012", @@ -29,7 +30,9 @@ export type ErrorCodes = keyof typeof errorCodes; export const makeApiProblem = makeApiProblemBuilder(errorCodes); -export function delegationNotFound(delegationId: string): ApiError { +export function delegationNotFound( + delegationId: DelegationId +): ApiError { return new ApiError({ detail: `Delegation ${delegationId} not found`, code: "delegationNotFound", @@ -38,9 +41,9 @@ export function delegationNotFound(delegationId: string): ApiError { } export function delegationAlreadyExists( - delegatorId: string, - eserviceId: string, - delegationKind: string + delegatorId: TenantId, + eserviceId: EServiceId, + delegationKind: DelegationKind ): ApiError { return new ApiError({ detail: `Delegation type ${delegationKind} already exists for EService ${eserviceId} by delegator ${delegatorId}`, @@ -89,7 +92,7 @@ export function tenantIsNotIPAError( } export function tenantNotAllowedToDelegation( - tenantId: string, + tenantId: TenantId, kind: DelegationKind ): ApiError { return new ApiError({ @@ -99,51 +102,44 @@ export function tenantNotAllowedToDelegation( }); } -export function delegationNotRevokable( - delegation: Delegation +export function operationRestrictedToDelegate( + tenantId: TenantId, + delegationId: DelegationId ): ApiError { return new ApiError({ - detail: `Delegation ${delegation.id} is not revokable. State: ${delegation.state}`, - code: "delegationNotRevokable", - title: "Delegation not revokable", + detail: `Tenant ${tenantId} is not a delegate for delegation ${delegationId}`, + code: "operationRestrictedToDelegate", + title: "Operation restricted to delegate", }); } -export function delegatorNotAllowToRevoke( - delegation: Delegation +export function operationRestrictedToDelegator( + tenantId: TenantId, + delegationId: DelegationId ): ApiError { return new ApiError({ - detail: `Requester ${delegation.id} is not delegator for the current delegation with id ${delegation.id}`, - code: "operationNotAllowOnDelegation", - title: "Requester and delegator are differents", - }); -} - -export function operationRestrictedToDelegate( - tenantId: string, - delegationId: string -): ApiError { - return new ApiError({ - detail: `Tenant ${tenantId} is not a delegate for delegation ${delegationId}`, - code: "operationRestrictedToDelegate", - title: "Operation restricted to delegate", + detail: `Tenant ${tenantId} is not a delegator for delegation ${delegationId}`, + code: "operationRestrictedToDelegator", + title: "Operation restricted to delegator", }); } export function incorrectState( - delegationId: string, + delegationId: DelegationId, actualState: DelegationState, - expectedState: DelegationState + expected: DelegationState | DelegationState[] ): ApiError { return new ApiError({ - detail: `Delegation ${delegationId} is in state ${actualState} but expected ${expectedState}`, + detail: `Delegation ${delegationId} is in state ${actualState} but expected ${ + Array.isArray(expected) ? expected.join(",") : expected + }`, code: "incorrectState", title: "Incorrect state", }); } export function differentEServiceProducer( - requesterId: string + requesterId: TenantId ): ApiError { return new ApiError({ detail: `Eservice producer if different from requester with id ${requesterId}`, @@ -151,3 +147,13 @@ export function differentEServiceProducer( title: "Operation not allowed", }); } + +export function delegationStampNotFound( + stamp: keyof Delegation["stamps"] +): ApiError { + return new ApiError({ + detail: `Delegation ${stamp} stamp not found`, + code: "stampNotFound", + title: "Stamp not found", + }); +} diff --git a/packages/delegation-process/src/model/domain/models.ts b/packages/delegation-process/src/model/domain/models.ts index 216dba9c8f..8248238990 100644 --- a/packages/delegation-process/src/model/domain/models.ts +++ b/packages/delegation-process/src/model/domain/models.ts @@ -1,8 +1,12 @@ import { + DelegationId, DelegationKind, DelegationState, + EService, EServiceId, + Tenant, TenantId, + UserId, } from "pagopa-interop-models"; export type GetDelegationsFilters = { @@ -12,3 +16,37 @@ export type GetDelegationsFilters = { delegationKind?: DelegationKind; states?: DelegationState[]; }; + +export type DelegationActivationPDFPayload = { + todayDate: string; + todayTime: string; + delegationId: DelegationId; + delegatorName: Tenant["name"]; + delegatorCode: Tenant["externalId"]["value"]; + delegateName: Tenant["name"]; + delegateCode: Tenant["externalId"]["value"]; + eserviceId: EService["id"]; + eserviceName: EService["name"]; + submitterId: UserId; + submissionDate: string; + submissionTime: string; + activatorId: UserId; + activationDate: string; + activationTime: string; +}; + +export type DelegationRevocationPDFPayload = { + todayDate: string; + todayTime: string; + delegationId: DelegationId; + delegatorName: Tenant["name"]; + delegatorCode: Tenant["externalId"]["value"]; + delegateName: Tenant["name"]; + delegateCode: Tenant["externalId"]["value"]; + eserviceId: EService["id"]; + eserviceName: EService["name"]; + submitterId: UserId; + revokerId: UserId; + revocationDate: string; + revocationTime: string; +}; diff --git a/packages/delegation-process/src/model/domain/toEvent.ts b/packages/delegation-process/src/model/domain/toEvent.ts index 8241c4daf9..922334154c 100644 --- a/packages/delegation-process/src/model/domain/toEvent.ts +++ b/packages/delegation-process/src/model/domain/toEvent.ts @@ -26,18 +26,17 @@ export function toCreateEventProducerDelegationSubmitted( } export function toCreateEventProducerDelegationRevoked( - delegation: Delegation, - version: number, + delegation: WithMetadata, correlationId: CorrelationId ): CreateEvent { return { - streamId: delegation.id, - version, + streamId: delegation.data.id, + version: delegation.metadata.version, event: { type: "ProducerDelegationRevoked", event_version: 2, data: { - delegation: toDelegationV2(delegation), + delegation: toDelegationV2(delegation.data), }, }, correlationId, diff --git a/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html index 603eb98290..f505b3d7cb 100644 --- a/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html +++ b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html @@ -4,7 +4,7 @@ - Deroga all'erogazione + Delega all'erogazione {{{paged-pdf-polyfill}}} @@ -35,7 +35,7 @@ -

Deroga all'erogazione

+

Delega all'erogazione

diff --git a/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html index e36b2480cc..ca6d302c2b 100644 --- a/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html +++ b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html @@ -49,7 +49,8 @@

Revoca della delega all’erogazione

{{delegateCode}} - di seguito "delegato all'erogazione") tramite l'operatore amministrativo con identificativo Area Riservata {{submitterId}}, per l'e-service - {{eServiceName}}, contraddistinto da id {{eServiceId}} + {{eserviceName}}, contraddistinto da id + {{eserviceId}} (di seguito “e-service”).
@@ -60,7 +61,7 @@

Registrazione

{{revocationTime}}, l’infrastruttura ha ricevuto la dichiarazione di revoca della delega (di seguito “dichiarazione di revoca”) inviata dal delegante, tramite l’operatore amministrativo con - identificativo Area Riservata {{submitterId}}. + identificativo Area Riservata {{revokerId}}.
diff --git a/packages/delegation-process/src/services/delegationContractBuilder.ts b/packages/delegation-process/src/services/delegationContractBuilder.ts index 1e20e21f4e..d55200ad73 100644 --- a/packages/delegation-process/src/services/delegationContractBuilder.ts +++ b/packages/delegation-process/src/services/delegationContractBuilder.ts @@ -17,6 +17,11 @@ import { Tenant, } from "pagopa-interop-models"; import { DelegationProcessConfig } from "../config/config.js"; +import { + DelegationActivationPDFPayload, + DelegationRevocationPDFPayload, +} from "../model/domain/models.js"; +import { assertStampExists } from "./validators.js"; const CONTENT_TYPE_PDF = "application/pdf"; const DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME = "Delega"; @@ -70,10 +75,13 @@ export const contractBuilder = { "activation" ); + assertStampExists(delegation.stamps, "activation"); + const submissionDate = dateAtRomeZone(delegation.stamps.submission.when); const submissionTime = timeAtRomeZone(delegation.stamps.submission.when); - - const pdfBuffer = await pdfGenerator.generate(templateFilePath, { + const activationDate = dateAtRomeZone(delegation.stamps.activation.when); + const activationTime = timeAtRomeZone(delegation.stamps.activation.when); + const activationContractPayload: DelegationActivationPDFPayload = { todayDate, todayTime, delegationId: delegation.id, @@ -81,15 +89,19 @@ export const contractBuilder = { delegatorCode: delegator.externalId.value, delegateName: delegate.name, delegateCode: delegate.externalId.value, + eserviceId: eservice.id, + eserviceName: eservice.name, submitterId: delegation.stamps.submission.who, - eServiceName: eservice.name, - eServiceId: eservice.id, submissionDate, submissionTime, - activationDate: todayDate, - activationTime: todayTime, - activatorId: delegate.id, - }); + activatorId: delegation.stamps.activation.who, + activationDate, + activationTime, + }; + const pdfBuffer = await pdfGenerator.generate( + templateFilePath, + activationContractPayload + ); const documentPath = await fileManager.storeBytes( { @@ -146,7 +158,11 @@ export const contractBuilder = { "revocation" ); - const pdfBuffer = await pdfGenerator.generate(templateFilePath, { + assertStampExists(delegation.stamps, "revocation"); + const revocationDate = dateAtRomeZone(delegation.stamps.revocation.when); + const revocationTime = timeAtRomeZone(delegation.stamps.revocation.when); + + const revocationContractPayload: DelegationRevocationPDFPayload = { todayDate, todayTime, delegationId: delegation.id, @@ -154,13 +170,17 @@ export const contractBuilder = { delegatorCode: delegator.externalId.value, delegateName: delegate.name, delegateCode: delegate.externalId.value, + eserviceId: eservice.id, + eserviceName: eservice.name, submitterId: delegation.stamps.submission.who, - eServiceName: eservice.name, - eServiceId: eservice.id, - revocationDate: todayDate, - revocationTime: todayTime, - activatorId: delegate.id, - }); + revokerId: delegation.stamps.revocation.who, + revocationDate, + revocationTime, + }; + const pdfBuffer = await pdfGenerator.generate( + templateFilePath, + revocationContractPayload + ); const documentPath = await fileManager.storeBytes( { diff --git a/packages/delegation-process/src/services/delegationProducerService.ts b/packages/delegation-process/src/services/delegationProducerService.ts index 72963b4127..a11d06fc75 100644 --- a/packages/delegation-process/src/services/delegationProducerService.ts +++ b/packages/delegation-process/src/services/delegationProducerService.ts @@ -30,7 +30,6 @@ import { import { config } from "../config/config.js"; import { ReadModelService } from "./readModelService.js"; import { - assertDelegationIsRevokable, assertDelegationNotExists, assertDelegatorIsNotDelegate, assertDelegatorIsProducer, @@ -38,6 +37,8 @@ import { assertIsDelegate, assertIsState, assertDelegatorAndDelegateIPA, + assertIsDelegator, + activeDelegationStates, } from "./validators.js"; import { contractBuilder } from "./delegationContractBuilder.js"; import { retrieveDelegationById } from "./delegationService.js"; @@ -126,27 +127,43 @@ export function delegationProducerServiceBuilder( async revokeProducerDelegation( delegationId: DelegationId, { authData, logger, correlationId }: WithLogger - ): Promise { + ): Promise { const delegatorId = unsafeBrandId(authData.organizationId); logger.info( `Revoking delegation ${delegationId} by producer ${delegatorId}` ); - const currentDelegation = await retrieveDelegationById( + const { data: delegation, metadata } = await retrieveDelegationById( readModelService, delegationId ); - assertDelegationIsRevokable(currentDelegation.data, delegatorId); + + assertIsDelegator(delegation, delegatorId); + assertIsState(activeDelegationStates, delegation); const [delegator, delegate, eservice] = await Promise.all([ - retrieveTenantById(currentDelegation.data.delegatorId), - retrieveTenantById(currentDelegation.data.delegateId), - retrieveEserviceById(currentDelegation.data.eserviceId), + retrieveTenantById(delegation.delegatorId), + retrieveTenantById(delegation.delegateId), + retrieveEserviceById(delegation.eserviceId), ]); + const now = new Date(); + const revokedDelegationWithoutContract = { + ...delegation, + state: delegationState.revoked, + revokedAt: now, + stamps: { + ...delegation.stamps, + revocation: { + who: authData.userId, + when: now, + }, + }, + }; + const revocationContract = await contractBuilder.createRevocationContract( { - delegation: currentDelegation.data, + delegation: revokedDelegationWithoutContract, delegator, delegate, eservice, @@ -157,30 +174,19 @@ export function delegationProducerServiceBuilder( } ); - const now = new Date(); const revokedDelegation = { - ...currentDelegation.data, - state: delegationState.revoked, - revokedAt: now, + ...revokedDelegationWithoutContract, revocationContract, - stamps: { - ...currentDelegation.data.stamps, - revocation: { - who: authData.userId, - when: now, - }, - }, }; - await repository.createEvent( toCreateEventProducerDelegationRevoked( - revokedDelegation, - currentDelegation.metadata.version, + { + data: revokedDelegation, + metadata, + }, correlationId ) ); - - return revokedDelegation; }, async approveProducerDelegation( delegationId: DelegationId, @@ -206,9 +212,23 @@ export function delegationProducerServiceBuilder( retrieveEserviceById(delegation.eserviceId), ]); + const now = new Date(); + const approvedDelegationWithoutContract: Delegation = { + ...delegation, + state: delegationState.active, + approvedAt: now, + stamps: { + ...delegation.stamps, + activation: { + who: authData.userId, + when: now, + }, + }, + }; + const activationContract = await contractBuilder.createActivationContract( { - delegation, + delegation: approvedDelegationWithoutContract, delegator, delegate, eservice, @@ -219,24 +239,15 @@ export function delegationProducerServiceBuilder( } ); - const now = new Date(); + const approvedDelegation = { + ...approvedDelegationWithoutContract, + activationContract, + }; await repository.createEvent( toCreateEventProducerDelegationApproved( { - data: { - ...delegation, - state: delegationState.active, - approvedAt: now, - activationContract, - stamps: { - ...delegation.stamps, - activation: { - who: authData.userId, - when: now, - }, - }, - }, + data: approvedDelegation, metadata, }, correlationId diff --git a/packages/delegation-process/src/services/validators.ts b/packages/delegation-process/src/services/validators.ts index e4060bff3f..71ae569c0e 100644 --- a/packages/delegation-process/src/services/validators.ts +++ b/packages/delegation-process/src/services/validators.ts @@ -2,6 +2,7 @@ import { Delegation, delegationKind, DelegationKind, + DelegationStamp, DelegationState, delegationState, EService, @@ -13,12 +14,12 @@ import { import { match } from "ts-pattern"; import { delegationAlreadyExists, - delegationNotRevokable, + delegationStampNotFound, delegatorAndDelegateSameIdError, - delegatorNotAllowToRevoke, differentEServiceProducer, incorrectState, operationRestrictedToDelegate, + operationRestrictedToDelegator, tenantIsNotIPAError, tenantNotAllowedToDelegation, } from "../model/domain/errors.js"; @@ -84,19 +85,6 @@ export const assertTenantAllowedToReceiveDelegation = ( } }; -export const assertDelegationIsRevokable = ( - delegation: Delegation, - expectedDelegatorId: TenantId -): void => { - if (delegation.delegatorId !== expectedDelegatorId) { - throw delegatorNotAllowToRevoke(delegation); - } - - if (!activeDelegationStates.includes(delegation.state)) { - throw delegationNotRevokable(delegation); - } -}; - export const assertDelegationNotExists = async ( delegator: Tenant, eserviceId: EServiceId, @@ -109,7 +97,7 @@ export const assertDelegationNotExists = async ( delegatorId, eserviceId, delegationKind, - states: [delegationState.active, delegationState.waitingForApproval], + states: activeDelegationStates, }); if (delegations.length > 0) { @@ -119,22 +107,41 @@ export const assertDelegationNotExists = async ( export const assertIsDelegate = ( delegation: Delegation, - delegateId: TenantId + requesterId: TenantId ): void => { - if (delegation.delegateId !== delegateId) { - throw operationRestrictedToDelegate(delegateId, delegation.id); + if (delegation.delegateId !== requesterId) { + throw operationRestrictedToDelegate(requesterId, delegation.id); + } +}; + +export const assertIsDelegator = ( + delegation: Delegation, + requesterId: TenantId +): void => { + if (delegation.delegatorId !== requesterId) { + throw operationRestrictedToDelegator(requesterId, delegation.id); } }; export const assertIsState = ( - state: DelegationState, + expected: DelegationState | DelegationState[], delegation: Delegation ): void => { - if (delegation.state !== state) { - throw incorrectState( - delegation.id, - delegation.state, - delegationState.waitingForApproval - ); + if ( + (!Array.isArray(expected) && delegation.state !== expected) || + (Array.isArray(expected) && !expected.includes(delegation.state)) + ) { + throw incorrectState(delegation.id, delegation.state, expected); } }; + +export function assertStampExists( + stamps: Delegation["stamps"], + stamp: S +): asserts stamps is Delegation["stamps"] & { + [key in S]: DelegationStamp; +} { + if (!stamps[stamp]) { + throw delegationStampNotFound(stamp); + } +} diff --git a/packages/delegation-process/src/utilities/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts index 00268107f2..8078c364fd 100644 --- a/packages/delegation-process/src/utilities/errorMappers.ts +++ b/packages/delegation-process/src/utilities/errorMappers.ts @@ -11,7 +11,6 @@ const { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_BAD_REQUEST, HTTP_STATUS_FORBIDDEN, - HTTP_STATUS_UNAUTHORIZED, HTTP_STATUS_CONFLICT, } = constants; @@ -40,6 +39,7 @@ export const createProducerDelegationErrorMapper = ( .with( "tenantIsNotIPAError", "tenantNotAllowedToDelegation", + "differentEserviceProducer", () => HTTP_STATUS_FORBIDDEN ) .with("delegationAlreadyExists", () => HTTP_STATUS_CONFLICT) @@ -50,17 +50,17 @@ export const revokeDelegationErrorMapper = ( ): number => match(error.code) .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) - .with("delegationNotRevokable", () => HTTP_STATUS_FORBIDDEN) - .with("operationNotAllowOnDelegation", () => HTTP_STATUS_UNAUTHORIZED) + .with("operationRestrictedToDelegator", () => HTTP_STATUS_FORBIDDEN) + .with("incorrectState", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); export const approveDelegationErrorMapper = ( error: ApiError ): number => match(error.code) + .with("delegationNotFound", () => HTTP_STATUS_NOT_FOUND) .with("operationRestrictedToDelegate", () => HTTP_STATUS_FORBIDDEN) - .with("incorrectState", () => HTTP_STATUS_BAD_REQUEST) + .with("incorrectState", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); export const rejectDelegationErrorMapper = approveDelegationErrorMapper; diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index 1605edf592..2b7a4f7f9e 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -1,4 +1,6 @@ /* eslint-disable functional/no-let */ +import { fileURLToPath } from "url"; +import path from "path"; import { decodeProtobufPayload, getMockDelegation, @@ -17,11 +19,14 @@ import { toDelegationV2, unsafeBrandId, delegationKind, + Delegation, } from "pagopa-interop-models"; import { delegationState } from "pagopa-interop-models"; import { + dateAtRomeZone, formatDateyyyyMMddHHmmss, genericLogger, + timeAtRomeZone, } from "pagopa-interop-commons"; import { delegationNotFound, @@ -29,7 +34,6 @@ import { incorrectState, } from "../src/model/domain/errors.js"; import { config } from "../src/config/config.js"; -import { contractBuilder } from "../src/services/delegationContractBuilder.js"; import { addOneDelegation, addOneTenant, @@ -38,7 +42,6 @@ import { fileManager, readLastDelegationEvent, pdfGenerator, - flushPDFMetadata, } from "./utils.js"; describe("approve producer delegation", () => { @@ -62,6 +65,7 @@ describe("approve producer delegation", () => { }); it("should approve delegation if validations succeed", async () => { + vi.spyOn(pdfGenerator, "generate"); const delegationId = generateId(); const authData = getRandomAuthData(delegate.id); @@ -92,66 +96,68 @@ describe("approve producer delegation", () => { payload: event.data, }); - const expectedContractFilePath = ( - await fileManager.listFiles(config.s3Bucket, genericLogger) - )[0]; - - const documentId = unsafeBrandId( - expectedContractFilePath.split("/")[2] + const expectedContractId = unsafeBrandId( + actualDelegation!.activationContract!.id ); - - const expectedDelegation = { - ...toDelegationV2({ - ...delegation, - state: delegationState.active, - approvedAt: currentExecutionTime, - stamps: { - ...delegation.stamps, - activation: { - who: authData.userId, - when: currentExecutionTime, - }, - }, - activationContract: { - id: documentId, - contentType: "application/pdf", - createdAt: currentExecutionTime, - name: `${formatDateyyyyMMddHHmmss( - currentExecutionTime - )}_delegation_activation_contract.pdf`, - path: expectedContractFilePath, - prettyName: "Delega", - }, - }), + const expectedContractName = `${formatDateyyyyMMddHHmmss( + currentExecutionTime + )}_delegation_activation_contract.pdf`; + const expectedContract = { + id: expectedContractId, + contentType: "application/pdf", + createdAt: currentExecutionTime, + name: expectedContractName, + path: `${config.delegationDocumentPath}/${delegation.id}/${expectedContractId}/${expectedContractName}`, + prettyName: "Delega", }; - expect(actualDelegation).toEqual(expectedDelegation); - const actualContract = await fileManager.get( - config.s3Bucket, - expectedContractFilePath, - genericLogger - ); + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(expectedContract.path); - const { path: expectedContractPath } = - await contractBuilder.createActivationContract({ - delegation, - delegator, - delegate, - eservice, - pdfGenerator, - fileManager, - config, - logger: genericLogger, - }); + const approvedDelegationWithoutContract: Delegation = { + ...delegation, + state: delegationState.active, + approvedAt: currentExecutionTime, + stamps: { + ...delegation.stamps, + activation: { + who: authData.userId, + when: currentExecutionTime, + }, + }, + }; - const expectedContract = await fileManager.get( - config.s3Bucket, - expectedContractPath, - genericLogger - ); + const expectedDelegation = toDelegationV2({ + ...approvedDelegationWithoutContract, + activationContract: expectedContract, + }); + expect(actualDelegation).toEqual(expectedDelegation); - expect(flushPDFMetadata(actualContract, currentExecutionTime)).toEqual( - flushPDFMetadata(expectedContract, currentExecutionTime) + expect(pdfGenerator.generate).toHaveBeenCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates", + "delegationApprovedTemplate.html" + ), + { + todayDate: dateAtRomeZone(currentExecutionTime), + todayTime: timeAtRomeZone(currentExecutionTime), + delegationId: approvedDelegationWithoutContract.id, + delegatorName: delegator.name, + delegatorCode: delegator.externalId.value, + delegateName: delegate.name, + delegateCode: delegate.externalId.value, + eserviceId: eservice.id, + eserviceName: eservice.name, + submitterId: approvedDelegationWithoutContract.stamps.submission.who, + submissionDate: dateAtRomeZone(currentExecutionTime), + submissionTime: timeAtRomeZone(currentExecutionTime), + activatorId: approvedDelegationWithoutContract.stamps.activation!.who, + activationDate: dateAtRomeZone(currentExecutionTime), + activationTime: timeAtRomeZone(currentExecutionTime), + } ); }); @@ -197,31 +203,34 @@ describe("approve producer delegation", () => { ); }); - it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { - const delegation = getMockDelegation({ - kind: delegationKind.delegatedProducer, - state: "Active", - delegateId: delegate.id, - delegatorId: delegator.id, - eserviceId: eservice.id, - }); - await addOneDelegation(delegation); + it.each( + Object.values(delegationState).filter( + (state) => state !== delegationState.waitingForApproval + ) + )( + "should throw incorrectState when delegation is in %s state", + async (state) => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + state, + delegateId: delegate.id, + delegatorId: delegator.id, + eserviceId: eservice.id, + }); + await addOneDelegation(delegation); - await expect( - delegationProducerService.approveProducerDelegation(delegation.id, { - authData: getRandomAuthData(delegate.id), - serviceName: "", - correlationId: generateId(), - logger: genericLogger, - }) - ).rejects.toThrow( - incorrectState( - delegation.id, - delegationState.active, - delegationState.waitingForApproval - ) - ); - }); + await expect( + delegationProducerService.approveProducerDelegation(delegation.id, { + authData: getRandomAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrow( + incorrectState(delegation.id, state, delegationState.waitingForApproval) + ); + } + ); it("should generete a pdf document for a delegation", async () => { const delegation = getMockDelegation({ diff --git a/packages/delegation-process/test/createProducerDelegation.test.ts b/packages/delegation-process/test/createProducerDelegation.test.ts index f3e42b73c5..5f4262d154 100644 --- a/packages/delegation-process/test/createProducerDelegation.test.ts +++ b/packages/delegation-process/test/createProducerDelegation.test.ts @@ -276,8 +276,8 @@ describe("create producer delegation", () => { }); it.each(activeDelegationStates)( - "should throw a delegationAlreadyExists error when a producer Delegation in state %s already exists with for same delegator, delegate and eservice", - async (validDelegationState) => { + "should throw a delegationAlreadyExists error when a producer Delegation in state %s already exists with same delegator, delegate and eservice", + async (activeDelegationState) => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { @@ -306,7 +306,7 @@ describe("create producer delegation", () => { delegateId: delegate.id, eserviceId: eservice.id, }), - state: validDelegationState, + state: activeDelegationState, }; await addOneTenant(delegate); @@ -451,7 +451,7 @@ describe("create producer delegation", () => { ).rejects.toThrowError(delegatorAndDelegateSameIdError()); }); - it("should throw an tenantIsNotIPAError error if delegator has externalId origin different from IPA", async () => { + it("should throw a tenantIsNotIPAError error if delegator has externalId origin different from IPA", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { @@ -491,7 +491,7 @@ describe("create producer delegation", () => { ).rejects.toThrowError(tenantIsNotIPAError(delegator, "Delegator")); }); - it("should throw an tenantIsNotIPAError error if delegate has externalId origin different from IPA", async () => { + it("should throw a tenantIsNotIPAError error if delegate has externalId origin different from IPA", async () => { const delegatorId = generateId(); const authData = getRandomAuthData(delegatorId); const delegator = { diff --git a/packages/delegation-process/test/rejectProducerDelegation.test.ts b/packages/delegation-process/test/rejectProducerDelegation.test.ts index 9fd50cbd59..682a162723 100644 --- a/packages/delegation-process/test/rejectProducerDelegation.test.ts +++ b/packages/delegation-process/test/rejectProducerDelegation.test.ts @@ -115,28 +115,31 @@ describe("reject producer delegation", () => { ); }); - it("should throw incorrectState when delegation is not in WaitingForApproval state", async () => { - const delegate = getMockTenant(); - const delegation = getMockDelegation({ - kind: delegationKind.delegatedProducer, - state: "Active", - delegateId: delegate.id, - }); - await addOneDelegation(delegation); + it.each( + Object.values(delegationState).filter( + (state) => state !== delegationState.waitingForApproval + ) + )( + "should throw incorrectState when delegation is in %s state", + async (state) => { + const delegate = getMockTenant(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + state, + delegateId: delegate.id, + }); + await addOneDelegation(delegation); - await expect( - delegationProducerService.rejectProducerDelegation(delegation.id, "", { - authData: getRandomAuthData(delegate.id), - serviceName: "", - correlationId: generateId(), - logger: genericLogger, - }) - ).rejects.toThrow( - incorrectState( - delegation.id, - delegationState.active, - delegationState.waitingForApproval - ) - ); - }); + await expect( + delegationProducerService.rejectProducerDelegation(delegation.id, "", { + authData: getRandomAuthData(delegate.id), + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrow( + incorrectState(delegation.id, state, delegationState.waitingForApproval) + ); + } + ); }); diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts index 1130cc07c3..904cdcab27 100644 --- a/packages/delegation-process/test/revokeProducerDelegation.test.ts +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -1,4 +1,5 @@ -import { fail } from "assert"; +import { fileURLToPath } from "url"; +import path from "path"; import { decodeProtobufPayload, getMockDelegation, @@ -13,97 +14,40 @@ import { delegationState, generateId, TenantId, - fromDelegationV2, EServiceId, unsafeBrandId, DelegationContractId, delegationKind, UserId, + toDelegationV2, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { + dateAtRomeZone, formatDateyyyyMMddHHmmss, genericLogger, + timeAtRomeZone, } from "pagopa-interop-commons"; import { delegationNotFound, - delegationNotRevokable, - delegatorNotAllowToRevoke, + incorrectState, + operationRestrictedToDelegator, } from "../src/model/domain/errors.js"; import { config } from "../src/config/config.js"; -import { contractBuilder } from "../src/services/delegationContractBuilder.js"; +import { + activeDelegationStates, + inactiveDelegationStates, +} from "../src/services/validators.js"; import { addOneDelegation, addOneEservice, addOneTenant, delegationProducerService, fileManager, - flushPDFMetadata, pdfGenerator, - readDelegationEventByVersion, + readLastDelegationEvent, } from "./utils.js"; -type DelegationStateSeed = - | { - delegationData: { - state: "Rejected"; - rejectedAt: Date; - rejectionReason: string; - }; - stamps: { - rejection: { - who: UserId; - when: Date; - }; - }; - } - | { - delegationData: { - state: "Revoked"; - revokedAt: Date; - }; - stamps: { - revocation: { - who: UserId; - when: Date; - }; - }; - }; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const getNotRevocableStateSeeds = (): DelegationStateSeed[] => { - const rejectionOrRevokeDate = new Date(); - rejectionOrRevokeDate.setMonth(new Date().getMonth() - 1); - - return [ - { - delegationData: { - state: delegationState.rejected, - rejectedAt: rejectionOrRevokeDate, - rejectionReason: "Test is a test stop", - }, - stamps: { - rejection: { - who: generateId(), - when: rejectionOrRevokeDate, - }, - }, - }, - { - delegationData: { - state: delegationState.revoked, - revokedAt: rejectionOrRevokeDate, - }, - stamps: { - revocation: { - who: generateId(), - when: rejectionOrRevokeDate, - }, - }, - }, - ]; -}; - describe("revoke producer delegation", () => { const TEST_EXECUTION_DATE = new Date(); @@ -116,9 +60,8 @@ describe("revoke producer delegation", () => { vi.useRealTimers(); }); - const notRevocableDelegationState = getNotRevocableStateSeeds(); - it("should revoke a delegation if it exists", async () => { + vi.spyOn(pdfGenerator, "generate"); const currentExecutionTime = new Date(); const eserviceId = generateId(); const delegatorId = generateId(); @@ -162,102 +105,85 @@ describe("revoke producer delegation", () => { await addOneDelegation(existentDelegation); - const actualDelegation = - await delegationProducerService.revokeProducerDelegation( - existentDelegation.id, - { - authData, - logger: genericLogger, - correlationId: generateId(), - serviceName: "DelegationServiceTest", - } - ); + await delegationProducerService.revokeProducerDelegation( + existentDelegation.id, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "DelegationServiceTest", + } + ); - const expectedContractFilePath = ( - await fileManager.listFiles(config.s3Bucket, genericLogger) - )[0]; + const event = await readLastDelegationEvent(existentDelegation.id); + expect(event.version).toBe("1"); - const documentId = unsafeBrandId( - expectedContractFilePath.split("/")[2] + const { delegation: actualDelegation } = decodeProtobufPayload({ + messageType: ProducerDelegationRevokedV2, + payload: event.data, + }); + + const expectedContractId = unsafeBrandId( + actualDelegation!.revocationContract!.id ); + const expectedContractName = `${formatDateyyyyMMddHHmmss( + currentExecutionTime + )}_delegation_revocation_contract.pdf`; + const expectedContract = { + id: expectedContractId, + contentType: "application/pdf", + createdAt: currentExecutionTime, + name: expectedContractName, + path: `${config.delegationDocumentPath}/${existentDelegation.id}/${expectedContractId}/${expectedContractName}`, + prettyName: "Revoca della delega", + }; + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(expectedContract.path); - const expectedDelegation: Delegation = { + const revokedDelegationWithoutContract: Delegation = { ...existentDelegation, state: delegationState.revoked, revokedAt: currentExecutionTime, stamps: { - submission: { - who: existentDelegation.stamps.submission.who, - when: delegationCreationDate, - }, - activation: { - who: existentDelegation.stamps.activation!.who, - when: delegationActivationDate, - }, + ...existentDelegation.stamps, revocation: { who: authData.userId, when: currentExecutionTime, }, }, - revocationContract: { - id: documentId, - contentType: "application/pdf", - createdAt: currentExecutionTime, - name: `${formatDateyyyyMMddHHmmss( - currentExecutionTime - )}_delegation_revocation_contract.pdf`, - path: expectedContractFilePath, - prettyName: "Revoca della delega", - }, }; + const expectedDelegation = toDelegationV2({ + ...revokedDelegationWithoutContract, + revocationContract: expectedContract, + }); expect(actualDelegation).toEqual(expectedDelegation); - const actualContract = await fileManager.get( - config.s3Bucket, - expectedContractFilePath, - genericLogger - ); - - const { path: expectedContractPath } = - await contractBuilder.createActivationContract({ - delegation: actualDelegation, - delegator, - delegate, - eservice, - pdfGenerator, - fileManager, - config, - logger: genericLogger, - }); - - const expectedContract = await fileManager.get( - config.s3Bucket, - expectedContractPath, - genericLogger - ); - - expect(flushPDFMetadata(actualContract, currentExecutionTime)).toEqual( - flushPDFMetadata(expectedContract, currentExecutionTime) - ); - - const lastDelegationEvent = await readDelegationEventByVersion( - actualDelegation.id, - 1 + expect(pdfGenerator.generate).toHaveBeenCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates", + "delegationRevokedTemplate.html" + ), + { + todayDate: dateAtRomeZone(currentExecutionTime), + todayTime: timeAtRomeZone(currentExecutionTime), + delegationId: revokedDelegationWithoutContract.id, + delegatorName: delegator.name, + delegatorCode: delegator.externalId.value, + delegateName: delegate.name, + delegateCode: delegate.externalId.value, + eserviceId: eservice.id, + eserviceName: eservice.name, + submitterId: revokedDelegationWithoutContract.stamps.submission.who, + revokerId: revokedDelegationWithoutContract.stamps.revocation!.who, + revocationDate: dateAtRomeZone(currentExecutionTime), + revocationTime: timeAtRomeZone(currentExecutionTime), + } ); - - const delegationEventPayload = decodeProtobufPayload({ - messageType: ProducerDelegationRevokedV2, - payload: lastDelegationEvent.data, - }).delegation; - if (!delegationEventPayload) { - return fail("DelegationRevokedV2 payload not found"); - } - - const delegationFromLastEvent = fromDelegationV2(delegationEventPayload); - - expect(lastDelegationEvent.version).toBe("1"); - expect(delegationFromLastEvent).toMatchObject(expectedDelegation); }); it("should throw a delegationNotFound if Delegation does not exist", async () => { @@ -274,39 +200,17 @@ describe("revoke producer delegation", () => { ).rejects.toThrow(delegationNotFound(delegationId)); }); - it("should throw a delegatorNotAllowToRevoke if Requester Id and DelegatorId are differents", async () => { - const currentExecutionTime = new Date(); - + it("should throw a delegatorNotAllowToRevoke if Requester is not Delegator", async () => { const delegatorId = generateId(); const delegateId = generateId(); const authData = getRandomAuthData(delegatorId); const delegationId = generateId(); - const delegationCreationDate = new Date(); - delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); - - const delegationApprovalDate = new Date(); - delegationApprovalDate.setMonth(currentExecutionTime.getMonth() - 1); - - const existentDelegation = { - ...getMockDelegation({ - kind: delegationKind.delegatedProducer, - id: delegationId, - delegateId, - }), - approvedAt: delegationApprovalDate, - submittedAt: delegationCreationDate, - stamps: { - submission: { - who: generateId(), - when: delegationCreationDate, - }, - approval: { - who: generateId(), - when: delegationApprovalDate, - }, - }, - }; + const existentDelegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + id: delegationId, + delegateId, + }); await addOneDelegation(existentDelegation); @@ -317,46 +221,25 @@ describe("revoke producer delegation", () => { correlationId: generateId(), serviceName: "DelegationServiceTest", }) - ).rejects.toThrow(delegatorNotAllowToRevoke(existentDelegation)); + ).rejects.toThrow( + operationRestrictedToDelegator(delegatorId, delegationId) + ); vi.useRealTimers(); }); - it.each(notRevocableDelegationState)( - "should throw a delegatorNotAllowToRevoke if delegation doesn't have revocable one of revocable states [Rejected,Revoked]", - async (notRevocableDelegationState: DelegationStateSeed) => { - const currentExecutionTime = new Date(); - + it.each(inactiveDelegationStates)( + "should throw incorrectState when delegation is in %s state", + async (state) => { const delegatorId = generateId(); const delegateId = generateId(); const authData = getRandomAuthData(delegatorId); - const delegationCreationDate = new Date(); - delegationCreationDate.setMonth(currentExecutionTime.getMonth() - 2); - - const delegationActivationDate = new Date(); - delegationActivationDate.setMonth(currentExecutionTime.getMonth() - 1); - - const existentDelegation: Delegation = { - ...getMockDelegation({ - kind: delegationKind.delegatedProducer, - delegatorId, - delegateId, - }), - approvedAt: delegationActivationDate, - submittedAt: delegationCreationDate, - stamps: { - submission: { - who: generateId(), - when: delegationCreationDate, - }, - activation: { - who: generateId(), - when: delegationActivationDate, - }, - ...notRevocableDelegationState.stamps, - }, - ...notRevocableDelegationState.delegationData, - }; + const existentDelegation: Delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegatorId, + delegateId, + state, + }); await addOneDelegation(existentDelegation); @@ -370,7 +253,13 @@ describe("revoke producer delegation", () => { serviceName: "DelegationServiceTest", } ) - ).rejects.toThrow(delegationNotRevokable(existentDelegation)); + ).rejects.toThrow( + incorrectState( + existentDelegation.id, + existentDelegation.state, + activeDelegationStates + ) + ); } ); }); diff --git a/packages/delegation-process/test/utils.ts b/packages/delegation-process/test/utils.ts index 94fbca3251..351c3dd655 100644 --- a/packages/delegation-process/test/utils.ts +++ b/packages/delegation-process/test/utils.ts @@ -24,7 +24,6 @@ import { launchPuppeteerBrowser, } from "pagopa-interop-commons"; import puppeteer, { Browser } from "puppeteer"; -import { PDFDocument } from "pdf-lib"; import { delegationProducerServiceBuilder } from "../src/services/delegationProducerService.js"; import { delegationServiceBuilder } from "../src/services/delegationService.js"; import { readModelServiceBuilder } from "../src/services/readModelService.js"; @@ -118,20 +117,3 @@ export const addOneTenant = async (tenant: Tenant): Promise => { export const addOneEservice = async (eservice: EService): Promise => { await writeInReadmodel(toReadModelEService(eservice), eservices); }; - -export const flushPDFMetadata = async ( - byteArray: Uint8Array, - currentExecutionTime: Date -): Promise => { - const pdfModified = await PDFDocument.load(byteArray); - // Remove metadata properties - pdfModified.setTitle(""); - pdfModified.setAuthor(""); - pdfModified.setSubject(""); - pdfModified.setKeywords([]); - pdfModified.setProducer(""); - pdfModified.setCreator(""); - pdfModified.setCreationDate(currentExecutionTime); - pdfModified.setModificationDate(currentExecutionTime); - return await pdfModified.save(); -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4cc300720..1c8db4739f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1793,9 +1793,6 @@ importers: pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test - pdf-lib: - specifier: 1.17.1 - version: 1.17.1 pg-promise: specifier: 11.8.0 version: 11.8.0 @@ -3856,12 +3853,6 @@ packages: '@pagopa/interop-outbound-models@1.0.10': resolution: {integrity: sha512-/TDVP8j+Q0ErpVs7MzH9LhfiEPcOzqea00um4sjQ9uuA6/Yg0G9A739bDOuaxih2wKpJ3hNPLnm9riM+YAz6Ow==} - '@pdf-lib/standard-fonts@1.0.0': - resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} - - '@pdf-lib/upng@1.0.1': - resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==} - '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -6412,9 +6403,6 @@ packages: package-json-from-dist@1.0.0: resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6481,9 +6469,6 @@ packages: pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - pdf-lib@1.17.1: - resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==} - pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} @@ -9177,14 +9162,6 @@ snapshots: ts-pattern: 5.2.0 zod: 3.23.8 - '@pdf-lib/standard-fonts@1.0.0': - dependencies: - pako: 1.0.11 - - '@pdf-lib/upng@1.0.1': - dependencies: - pako: 1.0.11 - '@pkgjs/parseargs@0.11.0': optional: true @@ -12319,8 +12296,6 @@ snapshots: package-json-from-dist@1.0.0: {} - pako@1.0.11: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -12369,13 +12344,6 @@ snapshots: pathval@1.1.1: {} - pdf-lib@1.17.1: - dependencies: - '@pdf-lib/standard-fonts': 1.0.0 - '@pdf-lib/upng': 1.0.1 - pako: 1.0.11 - tslib: 1.14.1 - pend@1.2.0: {} pg-cloudflare@1.1.1: From e525ee5113fe3582f7c7067655c26a6864f9595d Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:42:04 +0100 Subject: [PATCH 039/126] IMN-914 Fix platformstate-writers logic (#1148) --- .../src/consumerServiceV1.ts | 3 + .../src/consumerServiceV2.ts | 2 + .../src/utils.ts | 34 +- .../consumerServiceV1.integration.test.ts | 112 ++- .../consumerServiceV2.integration.test.ts | 99 ++- .../test/utils.test.ts | 8 + .../test/utils.ts | 10 +- .../src/consumerServiceV2.ts | 61 +- .../src/utils.ts | 8 +- .../src/consumerServiceV2.ts | 5 +- .../catalog-platformstate-writer/src/utils.ts | 144 ++- .../src/consumerServiceV2.ts | 85 +- .../purpose-platformstate-writer/src/utils.ts | 60 +- .../consumerServiceV1.integration.test.ts | 10 +- .../consumerServiceV2.integration.test.ts | 827 +++++++++++------- .../test/utils.test.ts | 144 +-- 16 files changed, 959 insertions(+), 653 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 3e3254bc4b..f557ec080f 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -176,6 +176,7 @@ const handleFirstActivation = async ( // token-generation-states await updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId: agreement.id, agreementState: agreement.state, dynamoDBClient, GSIPK_eserviceId_descriptorId, @@ -235,6 +236,7 @@ const handleActivationOrSuspension = async ( // token-generation-states await updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId: agreement.id, agreementState: agreement.state, dynamoDBClient, GSIPK_eserviceId_descriptorId, @@ -336,6 +338,7 @@ const handleUpgrade = async ( await updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId: agreement.id, agreementState: agreement.state, dynamoDBClient, GSIPK_eserviceId_descriptorId, diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index caa711eee0..0edf4c84fc 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -99,6 +99,7 @@ export async function handleMessageV2( // token-generation-states await updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId: agreement.id, agreementState: agreement.state, dynamoDBClient, GSIPK_eserviceId_descriptorId, @@ -223,6 +224,7 @@ export async function handleMessageV2( await updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId: agreement.id, agreementState: agreement.state, dynamoDBClient, GSIPK_eserviceId_descriptorId, diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index b42a26d449..11af0e0e28 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -132,7 +132,7 @@ export const updateAgreementStateInPlatformStatesEntry = async ( ":newVersion": { N: version.toString(), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, @@ -140,7 +140,7 @@ export const updateAgreementStateInPlatformStatesEntry = async ( "#state": "state", }, UpdateExpression: - "SET #state = :newState, version = :newVersion, updatedAt = :newUpdateAt", + "SET #state = :newState, version = :newVersion, updatedAt = :newUpdatedAt", TableName: config.tokenGenerationReadModelTableNamePlatform, ReturnValues: "NONE", }; @@ -163,7 +163,7 @@ export const updateAgreementStateOnTokenStatesEntries = async ({ for (const entry of entriesToUpdate) { const input: UpdateItemInput = { // ConditionExpression to avoid upsert - ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + ConditionExpression: "attribute_exists(PK)", Key: { PK: { S: entry.PK, @@ -173,12 +173,12 @@ export const updateAgreementStateOnTokenStatesEntries = async ({ ":newState": { S: agreementStateToItemState(agreementState), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET agreementState = :newState, updatedAt = :newUpdateAt", + "SET agreementState = :newState, updatedAt = :newUpdatedAt", TableName: config.tokenGenerationReadModelTableNameTokenGeneration, ReturnValues: "NONE", }; @@ -190,12 +190,14 @@ export const updateAgreementStateOnTokenStatesEntries = async ({ export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = async ({ entriesToUpdate, + agreementId, agreementState, dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, }: { entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + agreementId: AgreementId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; @@ -204,7 +206,9 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = for (const entry of entriesToUpdate) { const additionalDescriptorInfo = catalogEntry && - (!entry.GSIPK_eserviceId_descriptorId || !entry.descriptorState); + (!entry.descriptorState || + !entry.descriptorAudience || + !entry.descriptorVoucherLifespan); const additionalAttributesToSet: Record = additionalDescriptorInfo @@ -220,9 +224,6 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = ":descriptorVoucherLifespan": { N: catalogEntry.descriptorVoucherLifespan.toString(), }, - ":gsi": { - S: GSIPK_eserviceId_descriptorId, - }, } : {}; const input: UpdateItemInput = { @@ -234,18 +235,24 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = }, }, ExpressionAttributeValues: { + ":agreementId": { + S: agreementId, + }, + ":gsiEServiceIdDescriptorId": { + S: GSIPK_eserviceId_descriptorId, + }, ":newState": { S: agreementStateToItemState(agreementState), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, ...additionalAttributesToSet, }, UpdateExpression: - "SET agreementState = :newState, updatedAt = :newUpdateAt".concat( + "SET agreementId = :agreementId, agreementState = :newState, GSIPK_eserviceId_descriptorId = :gsiEServiceIdDescriptorId, updatedAt = :newUpdatedAt".concat( additionalDescriptorInfo - ? ", GSI_eservice_id_descriptor_id = :gsi, descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan" + ? ", descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan" : "" ), TableName: config.tokenGenerationReadModelTableNameTokenGeneration, @@ -323,12 +330,14 @@ export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ GSIPK_consumerId_eserviceId, + agreementId, agreementState, dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, }: { GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; + agreementId: AgreementId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; @@ -372,6 +381,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ await updateAgreementStateAndDescriptorInfoOnTokenStatesEntries({ entriesToUpdate: tokenStateEntries.data, + agreementId, agreementState, dynamoDBClient, GSIPK_eserviceId_descriptorId, diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 67c1696479..0ce46ae4e3 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -26,6 +26,7 @@ import { generateId, itemState, makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, makePlatformStatesAgreementPK, makePlatformStatesEServiceDescriptorPK, makeTokenGenerationStatesClientKidPurposePK, @@ -44,7 +45,7 @@ import { } from "pagopa-interop-commons-test"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; -import { config, sleep } from "./utils.js"; +import { config } from "./utils.js"; describe("integration tests V1 events", async () => { if (!config) { @@ -132,7 +133,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -205,6 +205,7 @@ describe("integration tests V1 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -218,10 +219,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -238,6 +239,10 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -246,12 +251,16 @@ describe("integration tests V1 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -306,6 +315,7 @@ describe("integration tests V1 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -319,10 +329,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -347,6 +357,10 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -355,12 +369,16 @@ describe("integration tests V1 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + agreementId: agreement.id, + GSIPK_eserviceId_descriptorId, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -453,7 +471,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -478,6 +495,10 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -486,19 +507,23 @@ describe("integration tests V1 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + agreementId: agreement.id, agreementState: itemState.active, descriptorState: catalogEntry.state, descriptorAudience: catalogEntry.descriptorAudience, descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + agreementId: agreement.id, agreementState: itemState.active, descriptorState: catalogEntry.state, descriptorAudience: catalogEntry.descriptorAudience, descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, + GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; @@ -624,7 +649,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -742,7 +766,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -866,6 +889,7 @@ describe("integration tests V1 events", async () => { agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -880,10 +904,10 @@ describe("integration tests V1 events", async () => { agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -899,6 +923,10 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: latestAgreement.eserviceId, + descriptorId: latestAgreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -908,12 +936,16 @@ describe("integration tests V1 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -1042,7 +1074,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -1072,7 +1103,7 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("add the entry if it doesn't exist", async () => { + it("should add the entry if it doesn't exist", async () => { const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, @@ -1128,6 +1159,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: generateId(), + }), }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -1141,10 +1176,13 @@ describe("integration tests V1 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: generateId(), + }), }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); // platform-states @@ -1169,17 +1207,37 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, + agreementState: itemState.active, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); @@ -1213,7 +1271,6 @@ describe("integration tests V1 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -1325,7 +1382,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1440,6 +1496,10 @@ describe("integration tests V1 events", async () => { agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }), }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -1454,10 +1514,13 @@ describe("integration tests V1 events", async () => { agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }), }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1481,12 +1544,14 @@ describe("integration tests V1 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + agreementId: latestAgreement.id, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + agreementId: latestAgreement.id, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; @@ -1529,7 +1594,6 @@ describe("integration tests V1 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -1641,7 +1705,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1756,6 +1819,10 @@ describe("integration tests V1 events", async () => { agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }), }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -1770,10 +1837,13 @@ describe("integration tests V1 events", async () => { agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }), }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1797,12 +1867,14 @@ describe("integration tests V1 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -1921,7 +1993,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -2056,7 +2127,6 @@ describe("integration tests V1 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV1(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index f1cdca56f4..08186fc1cd 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -32,6 +32,7 @@ import { generateId, itemState, makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, makePlatformStatesAgreementPK, makePlatformStatesEServiceDescriptorPK, makeTokenGenerationStatesClientKidPurposePK, @@ -50,7 +51,7 @@ import { } from "pagopa-interop-commons-test"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; -import { config, sleep } from "./utils.js"; +import { config } from "./utils.js"; describe("integration tests V2 events", async () => { if (!config) { @@ -138,7 +139,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -211,9 +211,9 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: generateId(), kid: `kid ${Math.random()}`, @@ -224,10 +224,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -244,6 +244,10 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -252,13 +256,17 @@ describe("integration tests V2 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + agreementId: agreement.id, agreementState: itemState.active, + GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + agreementId: agreement.id, agreementState: itemState.active, + GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; @@ -312,6 +320,7 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -325,10 +334,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -353,6 +362,10 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -361,12 +374,16 @@ describe("integration tests V2 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -459,7 +476,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -484,6 +500,10 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -492,6 +512,8 @@ describe("integration tests V2 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, descriptorState: catalogEntry.state, descriptorAudience: catalogEntry.descriptorAudience, @@ -501,6 +523,8 @@ describe("integration tests V2 events", async () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, agreementState: itemState.active, descriptorState: catalogEntry.state, descriptorAudience: catalogEntry.descriptorAudience, @@ -631,7 +655,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -698,7 +721,6 @@ describe("integration tests V2 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -810,7 +832,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -942,7 +963,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1013,7 +1033,6 @@ describe("integration tests V2 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -1125,7 +1144,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1257,7 +1275,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1328,7 +1345,6 @@ describe("integration tests V2 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -1440,7 +1456,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1572,7 +1587,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1643,7 +1657,6 @@ describe("integration tests V2 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -1755,7 +1768,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1887,7 +1899,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -1959,7 +1970,6 @@ describe("integration tests V2 events", async () => { agreement.id ); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedAgreementEntry = await readAgreementEntry( @@ -2071,7 +2081,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -2203,7 +2212,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { @@ -2326,7 +2334,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -2449,6 +2456,7 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_consumerId_eserviceId, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -2463,11 +2471,11 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_consumerId_eserviceId, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -2483,6 +2491,10 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, @@ -2492,12 +2504,16 @@ describe("integration tests V2 events", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -2626,7 +2642,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -2712,6 +2727,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: generateId(), + }), }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -2725,10 +2744,13 @@ describe("integration tests V2 events", async () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: generateId(), + }), }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); // platform-states @@ -2753,17 +2775,35 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); const retrievedTokenStateEntries = await readTokenStateEntriesByConsumerIdEserviceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId: agreement.id, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); @@ -2848,7 +2888,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -2974,7 +3013,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( @@ -3109,7 +3147,6 @@ describe("integration tests V2 events", async () => { }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await sleep(1000, mockDate); await handleMessageV2(message, dynamoDBClient); const retrievedEntry = await readAgreementEntry( diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index 67b950fc6d..edd0a33ec7 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -437,6 +437,7 @@ describe("utils", async () => { describe("updateAgreementStateAndDescriptorInfoOnTokenStates", async () => { it("should do nothing if previous entry doesn't exist", async () => { + const agreementId = generateId(); const eserviceId = generateId(); const descriptorId = generateId(); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ @@ -468,6 +469,7 @@ describe("utils", async () => { expect( updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId, agreementState: agreementState.archived, dynamoDBClient, GSIPK_eserviceId_descriptorId, @@ -481,6 +483,7 @@ describe("utils", async () => { }); it("should update state if previous entries exist", async () => { + const agreementId = generateId(); const eserviceId = generateId(); const descriptorId = generateId(); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ @@ -539,6 +542,7 @@ describe("utils", async () => { await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); await updateAgreementStateAndDescriptorInfoOnTokenStates({ GSIPK_consumerId_eserviceId, + agreementId, agreementState: agreementState.active, dynamoDBClient, GSIPK_eserviceId_descriptorId, @@ -552,6 +556,8 @@ describe("utils", async () => { const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, + agreementId, agreementState: itemState.active, updatedAt: new Date().toISOString(), descriptorState: catalogEntry.state, @@ -561,6 +567,8 @@ describe("utils", async () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, + agreementId, agreementState: itemState.active, updatedAt: new Date().toISOString(), descriptorState: catalogEntry.state, diff --git a/packages/agreement-platformstate-writer/test/utils.ts b/packages/agreement-platformstate-writer/test/utils.ts index fdc2e3c2c5..aca07c9cc6 100644 --- a/packages/agreement-platformstate-writer/test/utils.ts +++ b/packages/agreement-platformstate-writer/test/utils.ts @@ -1,11 +1,3 @@ -import { inject, vi } from "vitest"; +import { inject } from "vitest"; export const config = inject("tokenGenerationReadModelConfig"); - -export const sleep = (ms: number, mockDate = new Date()): Promise => - new Promise((resolve) => { - vi.useRealTimers(); - setTimeout(resolve, ms); - vi.useFakeTimers(); - vi.setSystemTime(mockDate); - }); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index b4cc629627..3cfcf28a5d 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -109,35 +109,38 @@ export async function handleMessageV2( purposeId, }), GSIPK_purposeId: purposeId, - ...((purposeEntry && { - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: client.consumerId, - eserviceId: purposeEntry.purposeEserviceId, - }), - purposeState: purposeEntry.state, - purposeVersionId: purposeEntry.purposeVersionId, - }) || - {}), - ...((purposeEntry && - agreementEntry && { - agreementId: extractAgreementIdFromAgreementPK( - agreementEntry.PK - ), - agreementState: agreementEntry.state, - GSIPK_eserviceId_descriptorId: - makeGSIPKEServiceIdDescriptorId({ - eserviceId: purposeEntry.purposeEserviceId, - descriptorId: agreementEntry.agreementDescriptorId, - }), - }) || - {}), - ...((catalogEntry && { - descriptorState: catalogEntry.state, - descriptorAudience: catalogEntry.descriptorAudience, - descriptorVoucherLifespan: - catalogEntry.descriptorVoucherLifespan, - }) || - {}), + ...(purposeEntry + ? { + GSIPK_consumerId_eserviceId: + makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + } + : {}), + ...(purposeEntry && agreementEntry + ? { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + } + : {}), + ...(catalogEntry + ? { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + } + : {}), }; await upsertTokenStateClientPurposeEntry( diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 1b81553deb..2e2055156b 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -744,12 +744,12 @@ export const setClientPurposeIdsInPlatformStatesEntry = async ( ":newVersion": { N: version.toString(), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET clientPurposesIds = :clientPurposesIds, updatedAt = :newUpdateAt, version = :newVersion", + "SET clientPurposesIds = :clientPurposesIds, updatedAt = :newUpdatedAt, version = :newVersion", TableName: config.tokenGenerationReadModelTableNamePlatform, ReturnValues: "NONE", }; @@ -1035,7 +1035,7 @@ const convertToExpressionAttributeValues = ( return { ...expressionAttributeValues, - ":newUpdateAt": { S: new Date().toISOString() }, + ":newUpdatedAt": { S: new Date().toISOString() }, }; }; @@ -1052,7 +1052,7 @@ const generateUpdateItemInputData = ( .map((key) => `${key} = :${key}`) .join(", "); - const updateExpression = `SET updatedAt = :newUpdateAt, ${updateExpressionTmp}`; + const updateExpression = `SET updatedAt = :newUpdatedAt, ${updateExpressionTmp}`; return { updateExpression, diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index 7043f7889e..68da3e38a7 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -18,6 +18,7 @@ import { deleteCatalogEntry, descriptorStateToItemState, readCatalogEntry, + updateDescriptorInfoInTokenGenerationStatesTable, updateDescriptorStateInPlatformStatesEntry, updateDescriptorStateInTokenGenerationStatesTable, updateDescriptorVoucherLifespanInPlatformStateEntry, @@ -80,9 +81,11 @@ export async function handleMessageV2( eserviceId: eservice.id, descriptorId: descriptor.id, }); - await updateDescriptorStateInTokenGenerationStatesTable( + await updateDescriptorInfoInTokenGenerationStatesTable( eserviceId_descriptorId, descriptorStateToItemState(descriptor.state), + descriptor.voucherLifespan, + descriptor.audience, dynamoDBClient ); }; diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index 924d39fd68..a66c1dbb13 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -132,7 +132,7 @@ export const updateDescriptorStateInPlatformStatesEntry = async ( ":newVersion": { N: version.toString(), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, @@ -140,7 +140,7 @@ export const updateDescriptorStateInPlatformStatesEntry = async ( "#state": "state", }, UpdateExpression: - "SET #state = :newState, version = :newVersion, updatedAt = :newUpdateAt", + "SET #state = :newState, version = :newVersion, updatedAt = :newUpdatedAt", TableName: config.tokenGenerationReadModelTableNamePlatform, ReturnValues: "NONE", }; @@ -168,12 +168,12 @@ export const updateDescriptorVoucherLifespanInPlatformStateEntry = async ( ":newVersion": { N: version.toString(), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET descriptorVoucherLifespan = :newVoucherLifespan, version = :newVersion, updatedAt = :newUpdateAt", + "SET descriptorVoucherLifespan = :newVoucherLifespan, version = :newVersion, updatedAt = :newUpdatedAt", TableName: config.tokenGenerationReadModelTableNamePlatform, ReturnValues: "NONE", }; @@ -222,7 +222,7 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( ); } - await updateDescriptorStateEntriesInTokenGenerationStatesTable( + await updateDescriptorStateInTokenGenerationStatesEntries( descriptorState, dynamoDBClient, tokenStateEntries.data @@ -250,6 +250,79 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( ); }; +export const updateDescriptorInfoInTokenGenerationStatesTable = async ( + eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + descriptorState: ItemState, + descriptorVoucherLifespan: number, + descriptorAudience: string[], + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( + eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record + ): Promise => { + const input: QueryInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + IndexName: "Descriptor", + KeyConditionExpression: `GSIPK_eserviceId_descriptorId = :gsiValue`, + ExpressionAttributeValues: { + ":gsiValue": { S: eserviceId_descriptorId }, + }, + ExclusiveStartKey: exclusiveStartKey, + }; + const command = new QueryCommand(input); + const data: QueryCommandOutput = await dynamoDBClient.send(command); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token state entries: result ${JSON.stringify(data)} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenStateEntries = z + .array(TokenGenerationStatesClientPurposeEntry) + .safeParse(unmarshalledItems); + + if (!tokenStateEntries.success) { + throw genericInternalError( + `Unable to parse token state entry item: result ${JSON.stringify( + tokenStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + await updateDescriptorInfoInTokenGenerationStatesEntries({ + descriptorState, + descriptorVoucherLifespan, + descriptorAudience, + dynamoDBClient, + entriesToUpdate: tokenStateEntries.data, + }); + + if (!data.LastEvaluatedKey) { + return tokenStateEntries.data; + } else { + return [ + ...tokenStateEntries.data, + ...(await runPaginatedQuery( + eserviceId_descriptorId, + dynamoDBClient, + data.LastEvaluatedKey + )), + ]; + } + } + }; + + return await runPaginatedQuery( + eserviceId_descriptorId, + dynamoDBClient, + undefined + ); +}; + export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, @@ -311,14 +384,14 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = await runPaginatedQuery(eserviceId_descriptorId, dynamoDBClient, undefined); }; -const updateDescriptorStateEntriesInTokenGenerationStatesTable = async ( +const updateDescriptorStateInTokenGenerationStatesEntries = async ( descriptorState: ItemState, dynamoDBClient: DynamoDBClient, entriesToUpdate: TokenGenerationStatesClientPurposeEntry[] ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { - ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + ConditionExpression: "attribute_exists(PK)", Key: { PK: { S: entry.PK, @@ -328,12 +401,59 @@ const updateDescriptorStateEntriesInTokenGenerationStatesTable = async ( ":newState": { S: descriptorState, }, - ":newUpdateAt": { + ":newUpdatedAt": { + S: new Date().toISOString(), + }, + }, + UpdateExpression: + "SET descriptorState = :newState, updatedAt = :newUpdatedAt", + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } +}; + +const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ + descriptorState, + descriptorVoucherLifespan, + descriptorAudience, + dynamoDBClient, + entriesToUpdate, +}: { + descriptorState: ItemState; + descriptorVoucherLifespan: number; + descriptorAudience: string[]; + dynamoDBClient: DynamoDBClient; + entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; +}): Promise => { + for (const entry of entriesToUpdate) { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: entry.PK, + }, + }, + ExpressionAttributeValues: { + ":descriptorState": { + S: descriptorState, + }, + ":descriptorAudience": { + L: descriptorAudience.map((item) => ({ + S: item, + })), + }, + ":descriptorVoucherLifespan": { + N: descriptorVoucherLifespan.toString(), + }, + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET descriptorState = :newState, updatedAt = :newUpdateAt", + "SET descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan, updatedAt = :newUpdatedAt", TableName: config.tokenGenerationReadModelTableNameTokenGeneration, ReturnValues: "NONE", }; @@ -349,7 +469,7 @@ const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { - ConditionExpression: "attribute_exists(GSIPK_eserviceId_descriptorId)", + ConditionExpression: "attribute_exists(PK)", Key: { PK: { S: entry.PK, @@ -359,12 +479,12 @@ const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( ":newVoucherLifespan": { N: voucherLifespan.toString(), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET descriptorVoucherLifespan = :newVoucherLifespan, updatedAt = :newUpdateAt", + "SET descriptorVoucherLifespan = :newVoucherLifespan, updatedAt = :newUpdatedAt", TableName: config.tokenGenerationReadModelTableNameTokenGeneration, ReturnValues: "NONE", }; diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts index 66e579324e..10c7c38794 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts @@ -29,57 +29,60 @@ export async function handleMessageV2( dynamoDBClient: DynamoDBClient ): Promise { await match(message) - .with({ type: "PurposeActivated" }, async (msg) => { - const { purpose, primaryKey, purposeState, existingPurposeEntry } = - await getPurposeData({ - dynamoDBClient, - purposeV2: msg.data.purpose, - msgType: msg.type, - }); + .with( + { type: "PurposeActivated" }, + { type: "PurposeVersionActivated" }, + async (msg) => { + const { purpose, primaryKey, purposeState, existingPurposeEntry } = + await getPurposeData({ + dynamoDBClient, + purposeV2: msg.data.purpose, + msgType: msg.type, + }); - const purposeVersion = getLastSuspendedOrActivatedPurposeVersion( - purpose.versions - ); + const purposeVersion = getLastSuspendedOrActivatedPurposeVersion( + purpose.versions + ); - if (existingPurposeEntry) { - if (existingPurposeEntry.version > msg.version) { - // Stops processing if the message is older than the purpose entry - return Promise.resolve(); + if (existingPurposeEntry) { + if (existingPurposeEntry.version > msg.version) { + // Stops processing if the message is older than the purpose entry + return Promise.resolve(); + } else { + // platform-states + await updatePurposeDataInPlatformStatesEntry({ + dynamoDBClient, + primaryKey, + purposeState, + purposeVersionId: purposeVersion.id, + version: msg.version, + }); + } } else { // platform-states - await updatePurposeDataInPlatformStatesEntry({ - dynamoDBClient, - primaryKey, - purposeState, + const purposeEntry: PlatformStatesPurposeEntry = { + PK: primaryKey, + state: purposeState, purposeVersionId: purposeVersion.id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, version: msg.version, - }); + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry(dynamoDBClient, purposeEntry); } - } else { - // platform-states - const purposeEntry: PlatformStatesPurposeEntry = { - PK: primaryKey, - state: purposeState, - purposeVersionId: purposeVersion.id, - purposeEserviceId: purpose.eserviceId, - purposeConsumerId: purpose.consumerId, - version: msg.version, - updatedAt: new Date().toISOString(), - }; - await writePlatformPurposeEntry(dynamoDBClient, purposeEntry); - } - // token-generation-states - await updateTokenEntriesWithPurposeAndPlatformStatesData( - dynamoDBClient, - purpose, - purposeState, - purposeVersion.id - ); - }) + // token-generation-states + await updateTokenEntriesWithPurposeAndPlatformStatesData( + dynamoDBClient, + purpose, + purposeState, + purposeVersion.id + ); + } + ) .with( { type: "NewPurposeVersionActivated" }, - { type: "PurposeVersionActivated" }, { type: "PurposeVersionSuspendedByConsumer" }, { type: "PurposeVersionSuspendedByProducer" }, { type: "PurposeVersionUnsuspendedByConsumer" }, diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index 48355bd8c0..de840e33e6 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -205,7 +205,7 @@ export const updatePurposeDataInPlatformStatesEntry = async ({ ":newVersion": { N: version.toString(), }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, @@ -213,7 +213,7 @@ export const updatePurposeDataInPlatformStatesEntry = async ({ "#state": "state", }, UpdateExpression: - "SET #state = :newState, version = :newVersion, updatedAt = :newUpdateAt, purposeVersionId = :newPurposeVersionId", + "SET #state = :newState, version = :newVersion, updatedAt = :newUpdatedAt, purposeVersionId = :newPurposeVersionId", TableName: config.tokenGenerationReadModelTableNamePlatform, ReturnValues: "NONE", }; @@ -261,50 +261,47 @@ export const updateTokenEntriesWithPurposeAndPlatformStatesData = async ( const tokenEntryPK = entry.PK; const isAgreementMissingInTokenTable = platformAgreementEntry && - (!entry.GSIPK_consumerId_eserviceId || - !entry.agreementId || - !entry.agreementState); + (!entry.agreementId || + !entry.agreementState || + !entry.GSIPK_eserviceId_descriptorId); // Agreement data from platform-states const agreementExpressionAttributeValues: Record = isAgreementMissingInTokenTable ? { - ":GSIPK_consumerId_eserviceId": { - S: platformAgreementEntry.GSIPK_consumerId_eserviceId, - }, ":agreementId": { S: extractAgreementIdFromAgreementPK(platformAgreementEntry.PK), }, ":agreementState": { S: platformAgreementEntry.state, }, + ":GSIPK_eserviceId_descriptorId": { + S: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: platformAgreementEntry.agreementDescriptorId, + }), + }, } : {}; const agreementUpdateExpression = isAgreementMissingInTokenTable - ? `, GSIPK_consumerId_eserviceId = :GSIPK_consumerId_eserviceId, - agreementId = :agreementId, - agreementState = :agreementState` + ? `, agreementId = :agreementId, + agreementState = :agreementState, + GSIPK_eserviceId_descriptorId = :GSIPK_eserviceId_descriptorId` : ""; // Descriptor data from platform-states const isDescriptorDataMissingInTokenTable = platformAgreementEntry && catalogEntry && - (!entry.GSIPK_eserviceId_descriptorId || - !entry.descriptorAudience || - !entry.descriptorState); + (!entry.descriptorAudience || + !entry.descriptorState || + !entry.descriptorVoucherLifespan); const descriptorExpressionAttributeValues: Record< string, AttributeValue > = isDescriptorDataMissingInTokenTable ? { - ":GSIPK_eserviceId_descriptorId": { - S: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose.eserviceId, - descriptorId: platformAgreementEntry.agreementDescriptorId, - }), - }, ":descriptorState": { S: catalogEntry.state, }, @@ -318,15 +315,14 @@ export const updateTokenEntriesWithPurposeAndPlatformStatesData = async ( }, } : {}; - const descriptorUpdateExpression = catalogEntry - ? `, GSIPK_eserviceId_descriptorId = :GSIPK_eserviceId_descriptorId, - descriptorState = :descriptorState, + const descriptorUpdateExpression = isDescriptorDataMissingInTokenTable + ? `, descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan` : ""; const input: UpdateItemInput = { - ConditionExpression: "attribute_exists(GSIPK_purposeId)", + ConditionExpression: "attribute_exists(PK)", Key: { PK: { S: tokenEntryPK, @@ -341,12 +337,18 @@ export const updateTokenEntriesWithPurposeAndPlatformStatesData = async ( ":newPurposeVersionId": { S: purposeVersionId, }, - ":newUpdateAt": { + ":GSIPK_consumerId_eserviceId": { + S: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }), + }, + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET purposeState = :newState, purposeVersionId = :newPurposeVersionId, updatedAt = :newUpdateAt" + + "SET purposeState = :newState, purposeVersionId = :newPurposeVersionId, GSIPK_consumerId_eserviceId = :GSIPK_consumerId_eserviceId, updatedAt = :newUpdatedAt" + agreementUpdateExpression + descriptorUpdateExpression, TableName: config.tokenGenerationReadModelTableNameTokenGeneration, @@ -401,7 +403,7 @@ export const updatePurposeDataInTokenEntries = async ({ for (const entry of result.tokenStateEntries) { const input: UpdateItemInput = { - ConditionExpression: "attribute_exists(GSIPK_purposeId)", + ConditionExpression: "attribute_exists(PK)", Key: { PK: { S: entry.PK, @@ -414,12 +416,12 @@ export const updatePurposeDataInTokenEntries = async ({ ":newPurposeVersionId": { S: purposeVersionId, }, - ":newUpdateAt": { + ":newUpdatedAt": { S: new Date().toISOString(), }, }, UpdateExpression: - "SET purposeState = :newState, updatedAt = :newUpdateAt, purposeVersionId = :newPurposeVersionId", + "SET purposeState = :newState, updatedAt = :newUpdatedAt, purposeVersionId = :newPurposeVersionId", TableName: config.tokenGenerationReadModelTableNameTokenGeneration, ReturnValues: "NONE", }; diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts index 1911e06b0e..74f0f65a9f 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -176,6 +176,7 @@ describe("integration tests for events V1", () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, + GSIPK_consumerId_eserviceId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -189,6 +190,7 @@ describe("integration tests for events V1", () => { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), GSIPK_purposeId: purposeId, purposeState, + GSIPK_consumerId_eserviceId: undefined, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); @@ -215,12 +217,17 @@ describe("integration tests for events V1", () => { expectedPlatformPurposeEntry ); - // token-generation-states; + // token-generation-states + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -228,6 +235,7 @@ describe("integration tests for events V1", () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts index 6f413bf122..acd17dd905 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -122,7 +122,7 @@ describe("integration tests for events V2", () => { ); const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { PK: purposeEntryPrimaryKey, - state: getPurposeStateFromPurposeVersions(purpose.versions), + state: itemState.active, purposeVersionId: purpose.versions[0].id, purposeEserviceId: purpose.eserviceId, purposeConsumerId: purpose.consumerId, @@ -176,6 +176,8 @@ describe("integration tests for events V2", () => { const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; @@ -189,6 +191,8 @@ describe("integration tests for events V2", () => { const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState, }; @@ -214,12 +218,17 @@ describe("integration tests for events V2", () => { expectedPlatformPurposeEntry ); - // token-generation-states; + // token-generation-states + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -227,6 +236,7 @@ describe("integration tests for events V2", () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -361,6 +371,8 @@ describe("integration tests for events V2", () => { const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; @@ -369,6 +381,8 @@ describe("integration tests for events V2", () => { const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; @@ -391,12 +405,17 @@ describe("integration tests for events V2", () => { expectedPlatformPurposeEntry ); - // token-generation-states; + // token-generation-states + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -404,6 +423,7 @@ describe("integration tests for events V2", () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -791,26 +811,96 @@ describe("integration tests for events V2", () => { ]) ); }); + }); - it("should do no operation if the table entry doesn't exist", async () => { + describe("PurposeVersionActivated", async () => { + it("should insert the entry in platform states and do no operation in token-generation-states", async () => { const messageVersion = 1; const purposeVersions: PurposeVersion[] = [ getMockPurposeVersion(purposeVersionState.active), - getMockPurposeVersion(purposeVersionState.waitingForApproval), ]; const purpose: Purpose = { ...getMockPurpose(), versions: purposeVersions, }; + const payload: PurposeVersionActivatedV2 = { + purpose: toPurposeV2(purpose), + versionId: purposeVersions[0].id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: purpose.id, + version: messageVersion, + type: "PurposeVersionActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purpose.id); + expect( + await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey) + ).toBeUndefined(); + + // token-generation-states + expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + + await handleMessageV2(message, dynamoDBClient); + + // platform-states + const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( + dynamoDBClient, + purposeEntryPrimaryKey + ); + const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey, + state: itemState.active, + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: messageVersion, + updatedAt: mockDate.toISOString(), + }; + expect(retrievedPlatformPurposeEntry).toEqual( + expectedPlatformPurposeEntry + ); + + // token-generation-states + expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + }); + + it("should insert the entry in platform states if it doesn't exist and update token generation states", async () => { + const messageVersion = 1; + const purpose: Purpose = { + ...getMockPurpose(), + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; const purposeId = purpose.id; + const purposeVersions = purpose.versions; const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); + const payload: PurposeVersionActivatedV2 = { + purpose: toPurposeV2(purpose), + versionId: purposeVersions[0].id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: purposeId, + version: messageVersion, + type: "PurposeVersionActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; // platform-states const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); - const previousRetrievedPlatformPurposeEntry = - await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey); - expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); + const previousPlatformPurposeEntry = await readPlatformPurposeEntry( + dynamoDBClient, + purposeEntryPrimaryKey + ); + expect(previousPlatformPurposeEntry).toBeUndefined(); // token-generation-states const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -821,6 +911,8 @@ describe("integration tests for events V2", () => { const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; @@ -834,87 +926,92 @@ describe("integration tests for events V2", () => { const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurpose: Purpose = { - ...purpose, - versions: [ - { - ...purposeVersions[0], - state: purposeVersionState.archived, - updatedAt: new Date(), - }, - { - ...purposeVersions[1], - state: purposeVersionState.active, - firstActivationAt: new Date(), - updatedAt: new Date(), - }, - ], - }; - - const payload: NewPurposeVersionActivatedV2 = { - purpose: toPurposeV2(updatedPurpose), - versionId: updatedPurpose.versions[1].id, - }; - const message: PurposeEventEnvelope = { - sequence_num: 1, - stream_id: purposeId, - version: messageVersion, - type: "NewPurposeVersionActivated", - event_version: 2, - data: payload, - log_date: new Date(), - }; - - expect( - async () => await handleMessageV2(message, dynamoDBClient) - ).not.toThrow(); + await handleMessageV2(message, dynamoDBClient); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, purposeEntryPrimaryKey ); - expect(retrievedPlatformPurposeEntry).toBeUndefined(); + const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey, + state: itemState.active, + purposeVersionId: purposeVersions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformPurposeEntry).toEqual( + expectedPlatformPurposeEntry + ); // token-generation-states + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + GSIPK_consumerId_eserviceId, + purposeState: itemState.active, + purposeVersionId: purposeVersions[0].id, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + GSIPK_consumerId_eserviceId, + purposeState: itemState.active, + purposeVersionId: purposeVersions[0].id, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); - }); - describe("PurposeVersionActivated", async () => { it("should do no operation if the existing table entry is more recent", async () => { const previousEntryVersion = 2; const messageVersion = 1; - - const purposeVersions: PurposeVersion[] = [ - getMockPurposeVersion(purposeVersionState.active), - ]; const purpose: Purpose = { ...getMockPurpose(), - versions: purposeVersions, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const payload: PurposeVersionActivatedV2 = { + purpose: toPurposeV2(purpose), + versionId: purpose.versions[0].id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: purpose.id, + version: messageVersion, + type: "PurposeVersionActivated", + event_version: 2, + data: payload, + log_date: new Date(), }; - const purposeId = purpose.id; - const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); // platform-states - const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); + const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purpose.id); const previousPlatformPurposeEntry: PlatformStatesPurposeEntry = { PK: purposeEntryPrimaryKey, - state: purposeState, - purposeVersionId: purposeVersions[0].id, + state: getPurposeStateFromPurposeVersions(purpose.versions), + purposeVersionId: purpose.versions[0].id, purposeEserviceId: purpose.eserviceId, purposeConsumerId: purpose.consumerId, version: previousEntryVersion, @@ -926,12 +1023,12 @@ describe("integration tests for events V2", () => { ); // token-generation-states + const purposeId = purpose.id; const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeState, - purposeVersionId: purposeVersions[0].id, + purposeState: itemState.inactive, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -939,37 +1036,10 @@ describe("integration tests for events V2", () => { { ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeState, - purposeVersionId: purposeVersions[0].id, + purposeState: itemState.inactive, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurpose: Purpose = { - ...purpose, - versions: [ - { - ...purposeVersions[0], - state: purposeVersionState.suspended, - suspendedAt: new Date(), - }, - ], - suspendedByConsumer: true, - }; - - const payload: PurposeVersionActivatedV2 = { - purpose: toPurposeV2(updatedPurpose), - versionId: purposeVersions[0].id, - }; - const message: PurposeEventEnvelope = { - sequence_num: 1, - stream_id: purposeId, - version: messageVersion, - type: "PurposeVersionActivated", - event_version: 2, - data: payload, - log_date: new Date(), - }; - await handleMessageV2(message, dynamoDBClient); // platform-states @@ -984,6 +1054,7 @@ describe("integration tests for events V2", () => { // token-generation-states const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ @@ -997,21 +1068,30 @@ describe("integration tests for events V2", () => { const previousEntryVersion = 2; const messageVersion = 3; - const purposeVersions: PurposeVersion[] = [ - getMockPurposeVersion(purposeVersionState.waitingForApproval), - ]; const purpose: Purpose = { ...getMockPurpose(), - versions: purposeVersions, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purposeVersions = purpose.versions; + const payload: PurposeVersionActivatedV2 = { + purpose: toPurposeV2(purpose), + versionId: purposeVersions[0].id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: purpose.id, + version: messageVersion, + type: "PurposeVersionActivated", + event_version: 2, + data: payload, + log_date: new Date(), }; - const purposeId = purpose.id; - const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); // platform-states - const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); + const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purpose.id); const previousPlatformPurposeEntry: PlatformStatesPurposeEntry = { PK: purposeEntryPrimaryKey, - state: purposeState, + state: getPurposeStateFromPurposeVersions(purpose.versions), purposeVersionId: purposeVersions[0].id, purposeEserviceId: purpose.eserviceId, purposeConsumerId: purpose.consumerId, @@ -1024,49 +1104,27 @@ describe("integration tests for events V2", () => { ); // token-generation-states + const purposeId = purpose.id; const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, - purposeState, - purposeVersionId: purposeVersions[0].id, + purposeState: itemState.inactive, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...getMockTokenStatesClientPurposeEntry(), + GSIPK_consumerId_eserviceId: undefined, + GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, - purposeState, - purposeVersionId: purposeVersions[0].id, + purposeState: itemState.inactive, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurpose: Purpose = { - ...purpose, - versions: [ - { - ...purposeVersions[0], - state: purposeVersionState.active, - updatedAt: new Date(), - }, - ], - }; - - const payload: PurposeVersionActivatedV2 = { - purpose: toPurposeV2(updatedPurpose), - versionId: purposeVersions[0].id, - }; - const message: PurposeEventEnvelope = { - sequence_num: 1, - stream_id: purposeId, - version: messageVersion, - type: "PurposeVersionActivated", - event_version: 2, - data: payload, - log_date: new Date(), - }; - await handleMessageV2(message, dynamoDBClient); // platform-states @@ -1085,11 +1143,16 @@ describe("integration tests for events V2", () => { ); // token-generation-states + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -1097,6 +1160,7 @@ describe("integration tests for events V2", () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), @@ -1110,32 +1174,68 @@ describe("integration tests for events V2", () => { ); }); - it("should do no operation if the table entry doesn't exist", async () => { + it("should update the token generation states entries with the corresponding agreement and descriptor data from platform states", async () => { const messageVersion = 1; const purpose: Purpose = { ...getMockPurpose(), - versions: [ - getMockPurposeVersion(purposeVersionState.waitingForApproval), - ], + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + const purposeVersions = purpose.versions; + const payload: PurposeVersionActivatedV2 = { + purpose: toPurposeV2(purpose), + versionId: purposeVersions[0].id, + }; + const message: PurposeEventEnvelope = { + sequence_num: 1, + stream_id: purpose.id, + version: messageVersion, + type: "PurposeVersionActivated", + event_version: 2, + data: payload, + log_date: new Date(), }; - const purposeId = purpose.id; - const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); // platform-states - const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); - const previousRetrievedPlatformPurposeEntry = - await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey); - expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); + const mockDescriptor = getMockDescriptor(); + const mockAgreement = { + ...getMockAgreement(purpose.eserviceId, purpose.consumerId), + descriptorId: mockDescriptor.id, + }; + const catalogAgreementEntryPK = makePlatformStatesAgreementPK( + mockAgreement.id + ); + const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ + consumerId: mockAgreement.consumerId, + eserviceId: mockAgreement.eserviceId, + }); + const previousAgreementEntry: PlatformStatesAgreementEntry = { + PK: catalogAgreementEntryPK, + state: itemState.active, + GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, + GSISK_agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: mockAgreement.descriptorId, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); - const updatedPurposeVersions: PurposeVersion[] = [ - { - ...getMockPurposeVersion(purposeVersionState.active), - updatedAt: new Date(), - }, - ]; + const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: mockDescriptor.id, + }); + const previousDescriptorEntry: PlatformStatesCatalogEntry = { + PK: catalogEntryPK, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: mockDescriptor.voucherLifespan, + version: 2, + updatedAt: new Date().toISOString(), + }; + await writeCatalogEntry(dynamoDBClient, previousDescriptorEntry); // token-generation-states + const purposeId = purpose.id; const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: generateId(), kid: `kid ${Math.random()}`, @@ -1145,7 +1245,15 @@ describe("integration tests for events V2", () => { { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), GSIPK_purposeId: purposeId, - purposeState, + purposeState: itemState.inactive, + GSIPK_consumerId_eserviceId: undefined, + agreementId: undefined, + agreementState: undefined, + GSIPK_eserviceId_descriptorId: undefined, + descriptorState: undefined, + descriptorAudience: undefined, + descriptorVoucherLifespan: undefined, + updatedAt: new Date().toISOString(), }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); @@ -1158,48 +1266,61 @@ describe("integration tests for events V2", () => { { ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), GSIPK_purposeId: purposeId, - purposeState, + purposeState: itemState.inactive, + GSIPK_consumerId_eserviceId: undefined, + agreementId: undefined, + agreementState: undefined, + GSIPK_eserviceId_descriptorId: undefined, + descriptorState: undefined, + descriptorAudience: undefined, + descriptorVoucherLifespan: undefined, + updatedAt: new Date().toISOString(), }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurpose: Purpose = { - ...purpose, - versions: updatedPurposeVersions, - }; - - const payload: PurposeVersionActivatedV2 = { - purpose: toPurposeV2(updatedPurpose), - versionId: updatedPurposeVersions[0].id, - }; - const message: PurposeEventEnvelope = { - sequence_num: 1, - stream_id: purposeId, - version: messageVersion, - type: "PurposeVersionActivated", - event_version: 2, - data: payload, - log_date: new Date(), - }; - - expect( - async () => await handleMessageV2(message, dynamoDBClient) - ).not.toThrow(); - - // platform-states - const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( - dynamoDBClient, - purposeEntryPrimaryKey - ); - expect(retrievedPlatformPurposeEntry).toBeUndefined(); + await handleMessageV2(message, dynamoDBClient); - // token-generation-states const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: mockDescriptor.id, + }); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + purposeState: itemState.active, + purposeVersionId: purposeVersions[0].id, + GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, + agreementId: mockAgreement.id, + agreementState: previousAgreementEntry.state, + GSIPK_eserviceId_descriptorId: gsiPKEserviceIdDescriptorId, + descriptorState: previousDescriptorEntry.state, + descriptorAudience: previousDescriptorEntry.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + purposeState: itemState.active, + purposeVersionId: purposeVersions[0].id, + GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, + agreementId: mockAgreement.id, + agreementState: previousAgreementEntry.state, + GSIPK_eserviceId_descriptorId: gsiPKEserviceIdDescriptorId, + descriptorState: previousDescriptorEntry.state, + descriptorAudience: previousDescriptorEntry.descriptorAudience, + descriptorVoucherLifespan: + previousDescriptorEntry.descriptorVoucherLifespan, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); @@ -1420,71 +1541,64 @@ describe("integration tests for events V2", () => { ); }); - it("should do no operation if the table entry doesn't exist", async () => { - const messageVersion = 1; + it("should update the entry if the message version is more recent and the purpose is suspended by the consumer and suspended by the producer", async () => { + const previousEntryVersion = 1; + const messageVersion = 2; + const purposeVersions: PurposeVersion[] = [ + getMockPurposeVersion(purposeVersionState.suspended), + ]; const purpose: Purpose = { ...getMockPurpose(), - versions: [getMockPurposeVersion(purposeVersionState.active)], + versions: purposeVersions, + suspendedByProducer: true, }; const purposeId = purpose.id; const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); - const purposeVersions: PurposeVersion[] = [ - getMockPurposeVersion(purposeVersionState.active), - ]; // platform-states const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); - const previousRetrievedPlatformPurposeEntry = - await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey); - expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); + const previousPlatformPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey, + state: purposeState, + purposeVersionId: purposeVersions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: previousEntryVersion, + updatedAt: mockDate.toISOString(), + }; + await writePlatformPurposeEntry( + dynamoDBClient, + previousPlatformPurposeEntry + ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurposeVersions: PurposeVersion[] = [ - { - ...purposeVersions[0], - state: purposeVersionState.suspended, - suspendedAt: new Date(), - }, - ]; - const updatedPurpose: Purpose = { ...purpose, - versions: updatedPurposeVersions, suspendedByConsumer: true, }; const payload: PurposeVersionSuspendedByConsumerV2 = { purpose: toPurposeV2(updatedPurpose), - versionId: updatedPurposeVersions[0].id, + versionId: purposeVersions[0].id, }; const message: PurposeEventEnvelope = { sequence_num: 1, @@ -1496,25 +1610,43 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - expect( - async () => await handleMessageV2(message, dynamoDBClient) - ).not.toThrow(); + await handleMessageV2(message, dynamoDBClient); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, purposeEntryPrimaryKey ); - expect(retrievedPlatformPurposeEntry).toBeUndefined(); + const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { + ...previousPlatformPurposeEntry, + state: itemState.inactive, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformPurposeEntry).toEqual( + expectedPlatformPurposeEntry + ); // token-generation-states const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); @@ -1735,71 +1867,64 @@ describe("integration tests for events V2", () => { ); }); - it("should do no operation if the table entry doesn't exist", async () => { - const messageVersion = 1; + it("should update the entry if the message version is more recent and the purpose is suspended by the producer and suspended by the consumer", async () => { + const previousEntryVersion = 1; + const messageVersion = 2; + const purposeVersions: PurposeVersion[] = [ + getMockPurposeVersion(purposeVersionState.suspended), + ]; const purpose: Purpose = { ...getMockPurpose(), - versions: [getMockPurposeVersion(purposeVersionState.active)], + versions: purposeVersions, + suspendedByConsumer: true, }; const purposeId = purpose.id; const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); - const purposeVersions: PurposeVersion[] = [ - getMockPurposeVersion(purposeVersionState.active), - ]; // platform-states const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); - const previousRetrievedPlatformPurposeEntry = - await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey); - expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); + const previousPlatformPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey, + state: purposeState, + purposeVersionId: purposeVersions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: previousEntryVersion, + updatedAt: mockDate.toISOString(), + }; + await writePlatformPurposeEntry( + dynamoDBClient, + previousPlatformPurposeEntry + ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurposeVersions: PurposeVersion[] = [ - { - ...purposeVersions[0], - state: purposeVersionState.suspended, - suspendedAt: new Date(), - }, - ]; - const updatedPurpose: Purpose = { ...purpose, - versions: updatedPurposeVersions, suspendedByProducer: true, }; const payload: PurposeVersionSuspendedByProducerV2 = { purpose: toPurposeV2(updatedPurpose), - versionId: updatedPurposeVersions[0].id, + versionId: purposeVersions[0].id, }; const message: PurposeEventEnvelope = { sequence_num: 1, @@ -1811,25 +1936,43 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - expect( - async () => await handleMessageV2(message, dynamoDBClient) - ).not.toThrow(); + await handleMessageV2(message, dynamoDBClient); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, purposeEntryPrimaryKey ); - expect(retrievedPlatformPurposeEntry).toBeUndefined(); + const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { + ...previousPlatformPurposeEntry, + state: itemState.inactive, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformPurposeEntry).toEqual( + expectedPlatformPurposeEntry + ); // token-generation-states const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); @@ -2054,73 +2197,65 @@ describe("integration tests for events V2", () => { ); }); - it("should do no operation if the table entry doesn't exist", async () => { - const messageVersion = 1; + it("should update the entry if the message version is more recent and the purpose is unsuspended by the consumer and suspended by the producer", async () => { + const previousEntryVersion = 1; + const messageVersion = 2; + const purposeVersions: PurposeVersion[] = [ + getMockPurposeVersion(purposeVersionState.suspended), + ]; const purpose: Purpose = { ...getMockPurpose(), - versions: [getMockPurposeVersion(purposeVersionState.suspended)], + versions: purposeVersions, + suspendedByProducer: true, suspendedByConsumer: true, }; const purposeId = purpose.id; const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); - const purposeVersions: PurposeVersion[] = [ - getMockPurposeVersion(purposeVersionState.active), - ]; // platform-states const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); - const previousRetrievedPlatformPurposeEntry = - await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey); - expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); + const previousPlatformPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey, + state: purposeState, + purposeVersionId: purposeVersions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: previousEntryVersion, + updatedAt: mockDate.toISOString(), + }; + await writePlatformPurposeEntry( + dynamoDBClient, + previousPlatformPurposeEntry + ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurposeVersions: PurposeVersion[] = [ - { - ...purposeVersions[0], - state: purposeVersionState.active, - suspendedAt: undefined, - updatedAt: new Date(), - }, - ]; - const updatedPurpose: Purpose = { ...purpose, - versions: updatedPurposeVersions, suspendedByConsumer: false, }; const payload: PurposeVersionUnsuspendedByConsumerV2 = { purpose: toPurposeV2(updatedPurpose), - versionId: updatedPurposeVersions[0].id, + versionId: purposeVersions[0].id, }; const message: PurposeEventEnvelope = { sequence_num: 1, @@ -2132,25 +2267,43 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - expect( - async () => await handleMessageV2(message, dynamoDBClient) - ).not.toThrow(); + await handleMessageV2(message, dynamoDBClient); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, purposeEntryPrimaryKey ); - expect(retrievedPlatformPurposeEntry).toBeUndefined(); + const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { + ...previousPlatformPurposeEntry, + state: itemState.inactive, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformPurposeEntry).toEqual( + expectedPlatformPurposeEntry + ); // token-generation-states const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); @@ -2375,73 +2528,65 @@ describe("integration tests for events V2", () => { ); }); - it("should do no operation if the table entry doesn't exist", async () => { - const messageVersion = 1; + it("should update the entry if the message version is more recent and the purpose is unsuspended by the producer and suspended by the consumer", async () => { + const previousEntryVersion = 1; + const messageVersion = 2; + const purposeVersions: PurposeVersion[] = [ + getMockPurposeVersion(purposeVersionState.suspended), + ]; const purpose: Purpose = { ...getMockPurpose(), - versions: [getMockPurposeVersion(purposeVersionState.suspended)], + versions: purposeVersions, + suspendedByConsumer: true, suspendedByProducer: true, }; const purposeId = purpose.id; const purposeState = getPurposeStateFromPurposeVersions(purpose.versions); - const purposeVersions: PurposeVersion[] = [ - getMockPurposeVersion(purposeVersionState.active), - ]; // platform-states const purposeEntryPrimaryKey = makePlatformStatesPurposePK(purposeId); - const previousRetrievedPlatformPurposeEntry = - await readPlatformPurposeEntry(dynamoDBClient, purposeEntryPrimaryKey); - expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); + const previousPlatformPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey, + state: purposeState, + purposeVersionId: purposeVersions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: previousEntryVersion, + updatedAt: mockDate.toISOString(), + }; + await writePlatformPurposeEntry( + dynamoDBClient, + previousPlatformPurposeEntry + ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenStatesClientPurposeEntry(), GSIPK_purposeId: purposeId, - purposeVersionId: purposeVersions[0].id, purposeState, + purposeVersionId: purposeVersions[0].id, }; await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - const updatedPurposeVersions: PurposeVersion[] = [ - { - ...purposeVersions[0], - state: purposeVersionState.active, - suspendedAt: undefined, - updatedAt: new Date(), - }, - ]; - const updatedPurpose: Purpose = { ...purpose, - versions: updatedPurposeVersions, suspendedByProducer: false, }; const payload: PurposeVersionUnsuspendedByProducerV2 = { purpose: toPurposeV2(updatedPurpose), - versionId: updatedPurposeVersions[0].id, + versionId: purposeVersions[0].id, }; const message: PurposeEventEnvelope = { sequence_num: 1, @@ -2453,25 +2598,43 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - expect( - async () => await handleMessageV2(message, dynamoDBClient) - ).not.toThrow(); + await handleMessageV2(message, dynamoDBClient); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, purposeEntryPrimaryKey ); - expect(retrievedPlatformPurposeEntry).toBeUndefined(); + const expectedPlatformPurposeEntry: PlatformStatesPurposeEntry = { + ...previousPlatformPurposeEntry, + state: itemState.inactive, + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformPurposeEntry).toEqual( + expectedPlatformPurposeEntry + ); // token-generation-states const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry1, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + { + ...previousTokenStateEntry2, + purposeState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; expect(retrievedTokenStateEntries).toHaveLength(2); expect(retrievedTokenStateEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + expectedTokenStateEntry1, + expectedTokenStateEntry2, ]) ); }); diff --git a/packages/purpose-platformstate-writer/test/utils.test.ts b/packages/purpose-platformstate-writer/test/utils.test.ts index 046ddd4b7b..c3e9d45011 100644 --- a/packages/purpose-platformstate-writer/test/utils.test.ts +++ b/packages/purpose-platformstate-writer/test/utils.test.ts @@ -725,12 +725,16 @@ describe("utils tests", async () => { newPurposeVersionId ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, updatedAt: new Date().toISOString(), @@ -738,6 +742,7 @@ describe("utils tests", async () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, updatedAt: new Date().toISOString(), @@ -823,11 +828,16 @@ describe("utils tests", async () => { newPurposeVersionId ); + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: mockDescriptor.id, + }); const retrievedTokenStateEntries = await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry1, + GSIPK_eserviceId_descriptorId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -837,6 +847,7 @@ describe("utils tests", async () => { const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { ...previousTokenStateEntry2, + GSIPK_eserviceId_descriptorId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -852,136 +863,7 @@ describe("utils tests", async () => { ); }); - it("should update entries with purpose state, version id and descriptor data if platform agreement and descriptor entry exist", async () => { - const purpose: Purpose = { - ...getMockPurpose(), - versions: [getMockPurposeVersion()], - }; - - // platform-states - const mockDescriptor = getMockDescriptor(); - const mockAgreement = { - ...getMockAgreement(purpose.eserviceId, purpose.consumerId), - descriptorId: mockDescriptor.id, - }; - const catalogAgreementEntryPK = makePlatformStatesAgreementPK( - mockAgreement.id - ); - const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ - consumerId: mockAgreement.consumerId, - eserviceId: mockAgreement.eserviceId, - }); - const previousAgreementEntry: PlatformStatesAgreementEntry = { - PK: catalogAgreementEntryPK, - state: itemState.active, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), - agreementDescriptorId: mockAgreement.descriptorId, - version: 2, - updatedAt: new Date().toISOString(), - }; - await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); - - const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ - eserviceId: purpose.eserviceId, - descriptorId: mockDescriptor.id, - }); - const previousDescriptorEntry: PlatformStatesCatalogEntry = { - PK: catalogEntryPK, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: mockDescriptor.voucherLifespan, - version: 2, - updatedAt: new Date().toISOString(), - }; - await writeCatalogEntry(dynamoDBClient, previousDescriptorEntry); - - // token-generation-states - const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - GSIPK_eserviceId_descriptorId: undefined, - descriptorState: undefined, - descriptorAudience: undefined, - descriptorVoucherLifespan: undefined, - }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - GSIPK_eserviceId_descriptorId: undefined, - descriptorState: undefined, - descriptorAudience: undefined, - descriptorVoucherLifespan: undefined, - updatedAt: new Date().toISOString(), - }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - - const newPurposeVersionId = generateId(); - await updateTokenEntriesWithPurposeAndPlatformStatesData( - dynamoDBClient, - purpose, - itemState.active, - newPurposeVersionId - ); - - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - - const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose.eserviceId, - descriptorId: mockDescriptor.id, - }); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, - purposeState: itemState.active, - purposeVersionId: newPurposeVersionId, - GSIPK_eserviceId_descriptorId: gsiPKEserviceIdDescriptorId, - descriptorState: previousDescriptorEntry.state, - descriptorAudience: previousDescriptorEntry.descriptorAudience, - descriptorVoucherLifespan: - previousDescriptorEntry.descriptorVoucherLifespan, - updatedAt: new Date().toISOString(), - }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry2, - purposeState: itemState.active, - purposeVersionId: newPurposeVersionId, - GSIPK_eserviceId_descriptorId: gsiPKEserviceIdDescriptorId, - descriptorState: previousDescriptorEntry.state, - descriptorAudience: previousDescriptorEntry.descriptorAudience, - descriptorVoucherLifespan: - previousDescriptorEntry.descriptorVoucherLifespan, - updatedAt: new Date().toISOString(), - }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( - expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, - ]) - ); - }); - - it("should update entries with purpose state, version id, agreement and descriptor data if platform agreement and descriptor entry exist", async () => { + it("should update entries with purpose state, version id, agreement and descriptor data if platform agreement and descriptor entries exist", async () => { const purpose: Purpose = { ...getMockPurpose(), versions: [getMockPurposeVersion()], From e958df11c0b3cdff3b33820781132d9bfb05280c Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 25 Nov 2024 16:02:54 +0100 Subject: [PATCH 040/126] IMN-787 Token details persister (#1094) --- package.json | 1 + .../authorizationServer.integration.test.ts | 2 +- .../test/authorizationServer.unit.test.ts | 2 +- .../src/config/consumerServiceConfig.ts | 15 ++ packages/kafka-iam-auth/src/index.ts | 95 +++++++--- packages/token-details-persister/.env | 18 ++ packages/token-details-persister/Dockerfile | 45 +++++ .../token-details-persister/aws.config.local | 4 + packages/token-details-persister/package.json | 42 +++++ .../src/config/config.ts | 27 +++ .../src/consumerService.ts | 38 ++++ packages/token-details-persister/src/index.ts | 22 +++ .../test/consumerService.integration.test.ts | 177 ++++++++++++++++++ .../test/tsconfig.json | 4 + .../token-details-persister/test/utils.ts | 10 + .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 + .../token-details-persister/tsconfig.json | 9 + .../token-details-persister/vitest.config.ts | 11 ++ pnpm-lock.yaml | 52 +++++ 20 files changed, 559 insertions(+), 25 deletions(-) create mode 100644 packages/token-details-persister/.env create mode 100644 packages/token-details-persister/Dockerfile create mode 100644 packages/token-details-persister/aws.config.local create mode 100644 packages/token-details-persister/package.json create mode 100644 packages/token-details-persister/src/config/config.ts create mode 100644 packages/token-details-persister/src/consumerService.ts create mode 100644 packages/token-details-persister/src/index.ts create mode 100644 packages/token-details-persister/test/consumerService.integration.test.ts create mode 100644 packages/token-details-persister/test/tsconfig.json create mode 100644 packages/token-details-persister/test/utils.ts create mode 100644 packages/token-details-persister/test/vitestGlobalSetup.ts create mode 100644 packages/token-details-persister/tsconfig.check.json create mode 100644 packages/token-details-persister/tsconfig.json create mode 100644 packages/token-details-persister/vitest.config.ts diff --git a/package.json b/package.json index e7ac4acb3b..0251107a56 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "start:delegation-readmodel-writer": "turbo start --filter pagopa-interop-delegation-readmodel-writer", "start:datalake-data-export": "turbo start --filter pagopa-interop-datalake-data-export", "start:authorization-server": "turbo start --filter pagopa-interop-authorization-server", + "start:token-details-persister": "turbo start --filter pagopa-interop-token-details-persister", "test": "turbo test", "build": "turbo build", "check": "turbo check", diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts index bae5e1ccf7..aaee1da260 100644 --- a/packages/authorization-server/test/authorizationServer.integration.test.ts +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -576,7 +576,7 @@ describe("authorization server tests", () => { genericLogger ); - const decodedFileContent = new TextDecoder().decode(fileContent); + const decodedFileContent = Buffer.from(fileContent).toString(); const parsedDecodedFileContent = JSON.parse(decodedFileContent); const expectedMessageBody: GeneratedTokenAuditDetails = { diff --git a/packages/authorization-server/test/authorizationServer.unit.test.ts b/packages/authorization-server/test/authorizationServer.unit.test.ts index ddfd05fc33..80c63c68e9 100644 --- a/packages/authorization-server/test/authorizationServer.unit.test.ts +++ b/packages/authorization-server/test/authorizationServer.unit.test.ts @@ -252,7 +252,7 @@ describe("unit tests", () => { const expectedFileContent = JSON.stringify(mockAuditMessage); - const decodedFileContent = new TextDecoder().decode(fileContent); + const decodedFileContent = Buffer.from(fileContent).toString(); expect(decodedFileContent).toEqual(expectedFileContent); }); diff --git a/packages/commons/src/config/consumerServiceConfig.ts b/packages/commons/src/config/consumerServiceConfig.ts index 6c5a058aaa..3c151e2f1d 100644 --- a/packages/commons/src/config/consumerServiceConfig.ts +++ b/packages/commons/src/config/consumerServiceConfig.ts @@ -20,6 +20,21 @@ export const KafkaConsumerConfig = KafkaConfig.and( ); export type KafkaConsumerConfig = z.infer; +export const KafkaBatchConsumerConfig = KafkaConsumerConfig.and( + z + .object({ + AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES: z.coerce.number(), + MESSAGES_TO_READ_PER_BATCH: z.coerce.number(), + MAX_WAIT_KAFKA_BATCH_MILLIS: z.coerce.number(), + }) + .transform((c) => ({ + averageKafkaMessageSizeInBytes: c.AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES, + messagesToReadPerBatch: c.MESSAGES_TO_READ_PER_BATCH, + maxWaitKafkaBatchMillis: c.MAX_WAIT_KAFKA_BATCH_MILLIS, + })) +); +export type KafkaBatchConsumerConfig = z.infer; + export const ReadModelWriterConfig = KafkaConsumerConfig.and(ReadModelDbConfig); export type ReadModelWriterConfig = z.infer; diff --git a/packages/kafka-iam-auth/src/index.ts b/packages/kafka-iam-auth/src/index.ts index 75272e53e1..3324468bdd 100644 --- a/packages/kafka-iam-auth/src/index.ts +++ b/packages/kafka-iam-auth/src/index.ts @@ -1,6 +1,8 @@ import { generateAuthToken } from "aws-msk-iam-sasl-signer-js"; import { Consumer, + ConsumerRunConfig, + EachBatchPayload, EachMessagePayload, Kafka, KafkaConfig, @@ -16,6 +18,7 @@ import { Logger, genericLogger, KafkaProducerConfig, + KafkaBatchConsumerConfig, } from "pagopa-interop-commons"; import { kafkaMessageProcessError } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; @@ -230,11 +233,17 @@ const initKafka = (config: InteropKafkaConfig): Kafka => { }); }; -const initConsumer = async ( - config: KafkaConsumerConfig, - topics: string[], - consumerHandler: (payload: EachMessagePayload) => Promise -): Promise => { +const initCustomConsumer = async ({ + config, + topics, + consumerRunConfig, + batchConfig, +}: { + config: KafkaConsumerConfig; + topics: string[]; + consumerRunConfig: (consumer: Consumer) => ConsumerRunConfig; + batchConfig?: { minBytes: number; maxWaitTimeInMs: number }; +}): Promise => { genericLogger.debug( `Consumer connecting to topics ${JSON.stringify(topics)}` ); @@ -252,6 +261,8 @@ const initConsumer = async ( return Promise.resolve(false); }, }, + ...batchConfig, + maxBytes: batchConfig ? batchConfig.minBytes * 1.25 : undefined, // TODO double-check }); if (config.resetConsumerOffsets) { @@ -276,23 +287,7 @@ const initConsumer = async ( genericLogger.info(`Consumer subscribed topic ${topics}`); - await consumer.run({ - autoCommit: false, - eachMessage: async (payload: EachMessagePayload) => { - try { - await consumerHandler(payload); - await kafkaCommitMessageOffsets(consumer, payload); - } catch (e) { - throw kafkaMessageProcessError( - payload.topic, - payload.partition, - payload.message.offset, - e - ); - } - }, - }); - + await consumer.run(consumerRunConfig(consumer)); return consumer; }; @@ -364,7 +359,61 @@ export const runConsumer = async ( consumerHandler: (messagePayload: EachMessagePayload) => Promise ): Promise => { try { - await initConsumer(config, topics, consumerHandler); + const consumerRunConfig = (consumer: Consumer): ConsumerRunConfig => ({ + autoCommit: false, + eachMessage: async (payload: EachMessagePayload): Promise => { + try { + await consumerHandler(payload); + await kafkaCommitMessageOffsets(consumer, payload); + } catch (e) { + throw kafkaMessageProcessError( + payload.topic, + payload.partition, + payload.message.offset, + e + ); + } + }, + }); + await initCustomConsumer({ config, topics, consumerRunConfig }); + } catch (e) { + genericLogger.error( + `Generic error occurs during consumer initialization: ${e}` + ); + processExit(); + } +}; + +export const runBatchConsumer = async ( + config: KafkaBatchConsumerConfig, + topics: string[], + consumerHandlerBatch: (messagePayload: EachBatchPayload) => Promise +): Promise => { + try { + const consumerRunConfig = (): ConsumerRunConfig => ({ + eachBatch: async (payload: EachBatchPayload): Promise => { + try { + await consumerHandlerBatch(payload); + } catch (e) { + throw kafkaMessageProcessError( + payload.batch.topic, + payload.batch.partition, + payload.batch.lastOffset().toString(), + e + ); + } + }, + }); + await initCustomConsumer({ + config, + topics, + consumerRunConfig, + batchConfig: { + minBytes: + config.averageKafkaMessageSizeInBytes * config.messagesToReadPerBatch, + maxWaitTimeInMs: config.maxWaitKafkaBatchMillis, + }, + }); } catch (e) { genericLogger.error( `Generic error occurs during consumer initialization: ${e}` diff --git a/packages/token-details-persister/.env b/packages/token-details-persister/.env new file mode 100644 index 0000000000..0a4a79e00d --- /dev/null +++ b/packages/token-details-persister/.env @@ -0,0 +1,18 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="TODO" +KAFKA_GROUP_ID="TODO" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +TOKEN_AUDITING_TOPIC="authorization-server.generated-jwt" +S3_BUCKET=interop-local-bucket +S3_CUSTOM_SERVER=true +S3_SERVER_HOST=http://localhost +S3_SERVER_PORT=9000 + +AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES=1000 +MESSAGES_TO_READ_PER_BATCH=5 +MAX_WAIT_KAFKA_BATCH_MILLIS=60000 + +AWS_CONFIG_FILE=aws.config.local +AWS_REGION="eu-south-1" diff --git a/packages/token-details-persister/Dockerfile b/packages/token-details-persister/Dockerfile new file mode 100644 index 0000000000..bdde83656a --- /dev/null +++ b/packages/token-details-persister/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/token-details-persister/package.json /app/packages/token-details-persister/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/token-details-persister /app/packages/token-details-persister +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/token-details-persister/node_modules \ + package*.json packages/token-details-persister/package*.json \ + packages/commons \ + packages/models \ + packages/kafka-iam-auth \ + packages/token-details-persister/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/token-details-persister +EXPOSE 3000 + +CMD ["node", "."] diff --git a/packages/token-details-persister/aws.config.local b/packages/token-details-persister/aws.config.local new file mode 100644 index 0000000000..34826a60e2 --- /dev/null +++ b/packages/token-details-persister/aws.config.local @@ -0,0 +1,4 @@ +[default] +aws_access_key_id=testawskey +aws_secret_access_key=testawssecret +region=eu-south-1 diff --git a/packages/token-details-persister/package.json b/packages/token-details-persister/package.json new file mode 100644 index 0000000000..a5f6375fb6 --- /dev/null +++ b/packages/token-details-persister/package.json @@ -0,0 +1,42 @@ +{ + "name": "pagopa-interop-token-details-persister", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability token-details-persister consumer service for auditing", + "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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "date-fns": "3.6.0", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "connection-string": "4.4.0", + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/token-details-persister/src/config/config.ts b/packages/token-details-persister/src/config/config.ts new file mode 100644 index 0000000000..914e1ceedc --- /dev/null +++ b/packages/token-details-persister/src/config/config.ts @@ -0,0 +1,27 @@ +import { + FileManagerConfig, + KafkaBatchConsumerConfig, + LoggerConfig, + S3Config, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +export const TokenDetailsPersisterConfig = FileManagerConfig.and(S3Config) + .and(KafkaBatchConsumerConfig) + .and(LoggerConfig) + .and( + z + .object({ + TOKEN_AUDITING_TOPIC: z.string(), + }) + .transform((c) => ({ + tokenAuditingTopic: c.TOKEN_AUDITING_TOPIC, + })) + ); + +export type TokenDetailsPersisterConfig = z.infer< + typeof TokenDetailsPersisterConfig +>; + +export const config: TokenDetailsPersisterConfig = + TokenDetailsPersisterConfig.parse(process.env); diff --git a/packages/token-details-persister/src/consumerService.ts b/packages/token-details-persister/src/consumerService.ts new file mode 100644 index 0000000000..311c3f428a --- /dev/null +++ b/packages/token-details-persister/src/consumerService.ts @@ -0,0 +1,38 @@ +import { + FileManager, + formatDateyyyyMMdd, + formatTimehhmmss, + Logger, +} from "pagopa-interop-commons"; +import { KafkaMessage } from "kafkajs"; +import { generateId } from "pagopa-interop-models"; +import { config } from "./config/config.js"; + +export async function handleMessages( + messages: KafkaMessage[], + fileManager: FileManager, + logger: Logger +): Promise { + const fileContent = messages.map((message) => message.value).join("\n"); + + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + const hmsTime = formatTimehhmmss(date); + + const fileName = `${ymdDate}_${hmsTime}_${generateId()}.ndjson`; + const filePath = `token-details/${ymdDate}`; + try { + await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: filePath, + name: fileName, + content: Buffer.from(fileContent), + }, + logger + ); + } catch (error) { + const message = error instanceof Error ? error.message : "generic error"; + throw Error(`Write operation failed - ${message}`); + } +} diff --git a/packages/token-details-persister/src/index.ts b/packages/token-details-persister/src/index.ts new file mode 100644 index 0000000000..22f77c13c7 --- /dev/null +++ b/packages/token-details-persister/src/index.ts @@ -0,0 +1,22 @@ +import { EachBatchPayload } from "kafkajs"; +import { initFileManager, logger } from "pagopa-interop-commons"; +import { runBatchConsumer } from "kafka-iam-auth"; +import { config } from "./config/config.js"; +import { handleMessages } from "./consumerService.js"; + +const fileManager = initFileManager(config); +const loggerInstance = logger({ + serviceName: "token-details-persister", +}); + +async function processMessage({ batch }: EachBatchPayload): Promise { + await handleMessages(batch.messages, fileManager, loggerInstance); + + loggerInstance.info( + `Auditing message was handled. Partition number: ${ + batch.partition + }. Offset: ${batch.firstOffset()} -> ${batch.lastOffset()}` + ); +} + +await runBatchConsumer(config, [config.tokenAuditingTopic], processMessage); diff --git a/packages/token-details-persister/test/consumerService.integration.test.ts b/packages/token-details-persister/test/consumerService.integration.test.ts new file mode 100644 index 0000000000..b825f42fec --- /dev/null +++ b/packages/token-details-persister/test/consumerService.integration.test.ts @@ -0,0 +1,177 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import crypto from "crypto"; +import { GeneratedTokenAuditDetails, generateId } from "pagopa-interop-models"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; +import { + formatDateyyyyMMdd, + formatTimehhmmss, + genericLogger, +} from "pagopa-interop-commons"; +import { KafkaMessage } from "kafkajs"; +import { handleMessages } from ".././src/consumerService.js"; +import { config } from "../src/config/config.js"; +import { fileManager } from "./utils.js"; + +describe("consumerService", () => { + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + const uuid = crypto.randomUUID(); + vi.spyOn(crypto, "randomUUID").mockReturnValue(uuid); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + it("should write one entry on the bucket", async () => { + const auditMessage = getMockAuditDetails(); + + const kafkaMessages: KafkaMessage[] = [ + { + key: Buffer.from(generateId()), + value: Buffer.from(JSON.stringify(auditMessage)), + timestamp: new Date().toISOString(), + offset: "0", + attributes: 1, + size: 100, + }, + ]; + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toMatchObject([]); + + await handleMessages(kafkaMessages, fileManager, genericLogger); + + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + const hmsTime = formatTimehhmmss(date); + const expectedFileName = `${ymdDate}_${hmsTime}_${generateId()}.ndjson`; + const expectedFilePathWithFileName = `token-details/${ymdDate}/${expectedFileName}`; + + const fileList = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + expect(fileList).toHaveLength(1); + expect(fileList).toMatchObject([expectedFilePathWithFileName]); + + const expectedFileContent = JSON.stringify(auditMessage); + + const fileContent = await fileManager.get( + config.s3Bucket, + expectedFilePathWithFileName, + genericLogger + ); + + const decodedFileContent = Buffer.from(fileContent).toString(); + expect(decodedFileContent).toMatchObject(expectedFileContent); + }); + + it("should write three entries on the bucket", async () => { + const auditMessages: GeneratedTokenAuditDetails[] = [ + getMockAuditDetails(), + getMockAuditDetails(), + getMockAuditDetails(), + ]; + + const kafkaMessages: KafkaMessage[] = auditMessages.map( + (auditMessage, index) => ({ + key: Buffer.from(generateId()), + value: Buffer.from(JSON.stringify(auditMessage)), + timestamp: new Date().toISOString(), + offset: index.toString(), + attributes: 1, + size: 100, + }) + ); + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toMatchObject([]); + + await handleMessages(kafkaMessages, fileManager, genericLogger); + + const date = new Date(); + const ymdDate = formatDateyyyyMMdd(date); + const hmsTime = formatTimehhmmss(date); + const expectedFileName = `${ymdDate}_${hmsTime}_${generateId()}.ndjson`; + const expectedFilePathWithFileName = `token-details/${ymdDate}/${expectedFileName}`; + + const fileList = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + + expect(fileList).toHaveLength(1); + expect(fileList).toMatchObject([expectedFilePathWithFileName]); + + const expectedFileContent = auditMessages + .map((auditingEntry) => JSON.stringify(auditingEntry)) + .join("\n"); + + const fileContent = await fileManager.get( + config.s3Bucket, + expectedFilePathWithFileName, + genericLogger + ); + + const decodedFileContent = Buffer.from(fileContent).toString(); + expect(decodedFileContent).toMatchObject(expectedFileContent); + }); + + it("should throw error if write operation fails", async () => { + const auditMessage = getMockAuditDetails(); + + const kafkaMessages: KafkaMessage[] = [ + { + key: Buffer.from(generateId()), + value: Buffer.from(JSON.stringify(auditMessage)), + timestamp: new Date().toISOString(), + offset: "0", + attributes: 1, + size: 100, + }, + ]; + + vi.spyOn(fileManager, "storeBytes").mockRejectedValueOnce(() => { + throw Error(); + }); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toMatchObject([]); + + expect( + handleMessages(kafkaMessages, fileManager, genericLogger) + ).rejects.toThrowError("Write operation failed - generic error"); + }); +}); + +const getMockAuditDetails = (): GeneratedTokenAuditDetails => ({ + correlationId: generateId(), + eserviceId: generateId(), + descriptorId: generateId(), + agreementId: generateId(), + subject: generateId(), + audience: "uat.interop.pagopa.it", + purposeId: generateId(), + algorithm: "RS256", + clientId: generateId(), + keyId: generateId(), + purposeVersionId: generateId(), + jwtId: generateId(), + issuedAt: new Date().getMilliseconds(), + issuer: "issuer", + expirationTime: new Date().getMilliseconds(), + organizationId: generateId(), + notBefore: new Date().getMilliseconds(), + clientAssertion: { + subject: generateId(), + audience: "uat.interop.pagopa.it", + algorithm: "RS256", + keyId: generateId(), + jwtId: generateId(), + issuedAt: new Date().getMilliseconds(), + issuer: "issuer", + expirationTime: new Date().getMilliseconds(), + }, +}); diff --git a/packages/token-details-persister/test/tsconfig.json b/packages/token-details-persister/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/token-details-persister/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/token-details-persister/test/utils.ts b/packages/token-details-persister/test/utils.ts new file mode 100644 index 0000000000..0e43908c32 --- /dev/null +++ b/packages/token-details-persister/test/utils.ts @@ -0,0 +1,10 @@ +import { inject, afterEach } from "vitest"; +import { setupTestContainersVitest } from "pagopa-interop-commons-test"; + +export const { cleanup, fileManager } = await setupTestContainersVitest( + undefined, + undefined, + inject("fileManagerConfig") +); + +afterEach(cleanup); diff --git a/packages/token-details-persister/test/vitestGlobalSetup.ts b/packages/token-details-persister/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..85a4c8ea41 --- /dev/null +++ b/packages/token-details-persister/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test/index.js"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/token-details-persister/tsconfig.check.json b/packages/token-details-persister/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/token-details-persister/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/token-details-persister/tsconfig.json b/packages/token-details-persister/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/token-details-persister/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/token-details-persister/vitest.config.ts b/packages/token-details-persister/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/token-details-persister/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 1c8db4739f..20656f47f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3022,6 +3022,58 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/token-details-persister: + dependencies: + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages: '@ampproject/remapping@2.3.0': From f59e34f4a44306b8b8b27c7793b393f0686bb4b5 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Mon, 25 Nov 2024 16:22:34 +0100 Subject: [PATCH 041/126] PIN-5678 - GET `/delegations/:delegationId/contracts/:contractId` in delegation-process (#1202) Co-authored-by: Eric Camellini Co-authored-by: AsterITA --- .../Retrieves a delegation contract.bru | 21 +++ packages/api-clients/open-api/bffApi.yml | 99 +++++++++++++ .../api-clients/open-api/delegationApi.yml | 46 +++++++ packages/api-clients/template-bff.hbs | 2 + packages/backend-for-frontend/.env | 1 + packages/backend-for-frontend/src/app.ts | 4 +- .../backend-for-frontend/src/config/config.ts | 2 + .../src/routers/delegationRouter.ts | 38 ++++- .../src/routers/producerDelegationRouter.ts | 7 +- .../src/services/delegationService.ts | 41 +++++- .../src/model/domain/errors.ts | 15 +- .../src/routers/DelegationRouter.ts | 37 +++++ .../src/services/delegationService.ts | 40 +++++- .../src/services/validators.ts | 13 ++ .../src/utilities/errorMappers.ts | 12 ++ .../test/getDelegationContract.test.ts | 130 ++++++++++++++++++ 16 files changed, 494 insertions(+), 14 deletions(-) create mode 100644 collections/delegation/Retrieves a delegation contract.bru create mode 100644 packages/delegation-process/test/getDelegationContract.test.ts diff --git a/collections/delegation/Retrieves a delegation contract.bru b/collections/delegation/Retrieves a delegation contract.bru new file mode 100644 index 0000000000..127071b5cd --- /dev/null +++ b/collections/delegation/Retrieves a delegation contract.bru @@ -0,0 +1,21 @@ +meta { + name: Retrieves a delegation contract + type: http + seq: 2 +} + +get { + url: {{host-delegation}}/delegations/:delegationId/contracts/:contractId + body: none + auth: none +} + +params:path { + delegationId: {{delegationId}} + contractId: {{contractId}} +} + +headers { + Authorization: {{JWT}} + X-Correlation-Id: {{correlation-id}} +} diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 0cd2c52c31..70261f707a 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -12721,6 +12721,105 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "/delegations/{delegationId}/contracts/{contractId}": + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + schema: + type: string + format: uuid + required: true + - name: contractId + in: path + schema: + type: string + format: uuid + required: true + get: + tags: + - delegations + summary: Retrieve a contract of a delegation + description: Retrieve a contract of a delegation + operationId: getDelegationContract + responses: + "200": + description: Contract retrieved + content: + application/octet-stream: + schema: + type: string + format: binary + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "404": + description: Resource Not Found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + "429": + description: Too Many Requests + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available "/status": get: security: [] diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml index 409dfeb45c..88d3061ecc 100644 --- a/packages/api-clients/open-api/delegationApi.yml +++ b/packages/api-clients/open-api/delegationApi.yml @@ -160,6 +160,52 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + /delegations/{delegationId}/contracts/{contractId}: + get: + description: Retrieves a delegation contract + summary: Retrieves a delegation contract + tags: + - delegation + operationId: getDelegationContract + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + - name: delegationId + in: path + description: The delegation id + required: true + schema: + type: string + - name: contractId + in: path + description: The delegation contract id + required: true + schema: + type: string + responses: + "200": + description: Delegation contract retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/DelegationContractDocument" + "401": + description: Unauthorized + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: Delegation contract not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /producer/delegations: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" diff --git a/packages/api-clients/template-bff.hbs b/packages/api-clients/template-bff.hbs index a194e18ce6..9779bf61f5 100644 --- a/packages/api-clients/template-bff.hbs +++ b/packages/api-clients/template-bff.hbs @@ -73,6 +73,8 @@ export const {{@key}}Endpoints = makeApi([ response: z.instanceof(Buffer), {{else if (and (eq method "get") (eq path "/eservices/:eServiceId/descriptors/:descriptorId/documents/:documentId"))}} response: z.instanceof(Buffer), + {{else if (and (eq method "get") (eq path "/delegations/:delegationId/contracts/:contractId"))}} + response: z.instanceof(Buffer), {{else}} response: {{{response}}}, {{/if}} diff --git a/packages/backend-for-frontend/.env b/packages/backend-for-frontend/.env index 4457ca94c7..1581a64840 100644 --- a/packages/backend-for-frontend/.env +++ b/packages/backend-for-frontend/.env @@ -80,4 +80,5 @@ IMPORT_ESERVICE_PATH="local/eservices-import" PRESIGNED_URL_GET_DURATION_MINUTES=5000 PRESIGNED_URL_PUT_DURATION_MINUTES= 5000 +DELEGATION_CONTRACTS_CONTAINER="interop-local-bucket" CLIENT_ASSERTION_AUDIENCE="dev.interop.pagopa.it" diff --git a/packages/backend-for-frontend/src/app.ts b/packages/backend-for-frontend/src/app.ts index 0a5f810292..4f6fcc9801 100644 --- a/packages/backend-for-frontend/src/app.ts +++ b/packages/backend-for-frontend/src/app.ts @@ -92,8 +92,8 @@ app.use( clientRouter(zodiosCtx, clients), privacyNoticeRouter(zodiosCtx), producerKeychainRouter(zodiosCtx, clients), - delegationRouter(zodiosCtx, clients), - producerDelegationRouter(zodiosCtx, clients) + delegationRouter(zodiosCtx, clients, fileManager), + producerDelegationRouter(zodiosCtx, clients, fileManager) ); export default app; diff --git a/packages/backend-for-frontend/src/config/config.ts b/packages/backend-for-frontend/src/config/config.ts index 04c1132655..6ee5e8f0bd 100644 --- a/packages/backend-for-frontend/src/config/config.ts +++ b/packages/backend-for-frontend/src/config/config.ts @@ -102,9 +102,11 @@ export type AuthorizationProcessServerConfig = z.infer< export const DelegationProcessServerConfig = z .object({ DELEGATION_PROCESS_URL: APIEndpoint, + DELEGATION_CONTRACTS_CONTAINER: z.string(), }) .transform((c) => ({ delegationProcessUrl: c.DELEGATION_PROCESS_URL, + delegationContractsContainer: c.DELEGATION_CONTRACTS_CONTAINER, })); export type DelegationProcessServerConfig = z.infer< typeof DelegationProcessServerConfig diff --git a/packages/backend-for-frontend/src/routers/delegationRouter.ts b/packages/backend-for-frontend/src/routers/delegationRouter.ts index 06d3a10f86..785c976892 100644 --- a/packages/backend-for-frontend/src/routers/delegationRouter.ts +++ b/packages/backend-for-frontend/src/routers/delegationRouter.ts @@ -2,6 +2,7 @@ import { ZodiosRouter } from "@zodios/express"; import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ExpressContext, + FileManager, ZodiosContext, zodiosValidationErrorToApiProblem, } from "pagopa-interop-commons"; @@ -10,7 +11,7 @@ import { unsafeBrandId } from "pagopa-interop-models"; import { PagoPAInteropBeClients } from "../clients/clientsProvider.js"; import { fromBffAppContext } from "../utilities/context.js"; import { delegationServiceBuilder } from "../services/delegationService.js"; -import { makeApiProblem } from "../model/errors.js"; +import { emptyErrorMapper, makeApiProblem } from "../model/errors.js"; import { getDelegationByIdErrorMapper, getDelegationsErrorMapper, @@ -22,7 +23,8 @@ const delegationRouter = ( delegationProcessClient, tenantProcessClient, catalogProcessClient, - }: PagoPAInteropBeClients + }: PagoPAInteropBeClients, + fileManager: FileManager ): ZodiosRouter => { const delegationRouter = ctx.router(bffApi.delegationsApi.api, { validationErrorHandler: zodiosValidationErrorToApiProblem, @@ -31,7 +33,8 @@ const delegationRouter = ( const delegationService = delegationServiceBuilder( delegationProcessClient, tenantProcessClient, - catalogProcessClient + catalogProcessClient, + fileManager ); delegationRouter @@ -97,7 +100,34 @@ const delegationRouter = ( return res.status(errorRes.status).send(errorRes); } - }); + }) + .get( + "/delegations/:delegationId/contracts/:contractId", + async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + const { delegationId, contractId } = req.params; + + try { + const result = await delegationService.getDelegationContract( + unsafeBrandId(delegationId), + unsafeBrandId(contractId), + ctx + ); + + return res.status(200).send(result); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving contract ${req.params.contractId} of delegation ${req.params.delegationId}` + ); + + return res.status(errorRes.status).send(errorRes); + } + } + ); return delegationRouter; }; diff --git a/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts b/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts index 97c8a86814..4e96905101 100644 --- a/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts +++ b/packages/backend-for-frontend/src/routers/producerDelegationRouter.ts @@ -2,6 +2,7 @@ import { ZodiosRouter } from "@zodios/express"; import { ZodiosEndpointDefinitions } from "@zodios/core"; import { ExpressContext, + FileManager, ZodiosContext, zodiosValidationErrorToApiProblem, } from "pagopa-interop-commons"; @@ -18,7 +19,8 @@ const producerDelegationRouter = ( delegationProcessClient, tenantProcessClient, catalogProcessClient, - }: PagoPAInteropBeClients + }: PagoPAInteropBeClients, + fileManager: FileManager ): ZodiosRouter => { const producerDelegationRouter = ctx.router( bffApi.producerDelegationsApi.api, @@ -29,7 +31,8 @@ const producerDelegationRouter = ( const delegationService = delegationServiceBuilder( delegationProcessClient, tenantProcessClient, - catalogProcessClient + catalogProcessClient, + fileManager ); producerDelegationRouter diff --git a/packages/backend-for-frontend/src/services/delegationService.ts b/packages/backend-for-frontend/src/services/delegationService.ts index 3d127d8873..f682224c60 100644 --- a/packages/backend-for-frontend/src/services/delegationService.ts +++ b/packages/backend-for-frontend/src/services/delegationService.ts @@ -5,8 +5,16 @@ import { delegationApi, tenantApi, } from "pagopa-interop-api-clients"; -import { getAllFromPaginated, WithLogger } from "pagopa-interop-commons"; -import { DelegationId, delegationKind } from "pagopa-interop-models"; +import { + FileManager, + getAllFromPaginated, + WithLogger, +} from "pagopa-interop-commons"; +import { + DelegationContractId, + DelegationId, + delegationKind, +} from "pagopa-interop-models"; import { DelegationsQueryParams, toBffDelegationApiCompactDelegation, @@ -20,6 +28,7 @@ import { } from "../clients/clientsProvider.js"; import { delegationNotFound } from "../model/errors.js"; import { BffAppContext, Headers } from "../utilities/context.js"; +import { config } from "../config/config.js"; // eslint-disable-next-line max-params async function enhanceDelegation< @@ -151,7 +160,8 @@ export async function getAllDelegations( export function delegationServiceBuilder( delegationClients: DelegationProcessClient, tenantClient: TenantProcessClient, - catalogClient: CatalogProcessClient + catalogClient: CatalogProcessClient, + fileManager: FileManager ) { return { async getDelegationById( @@ -237,6 +247,31 @@ export function delegationServiceBuilder( }, }; }, + async getDelegationContract( + delegationId: DelegationId, + contractId: DelegationContractId, + { headers, logger }: WithLogger + ): Promise { + logger.info( + `Retrieving delegation contract ${contractId} from delegation ${delegationId}` + ); + + const contract = await delegationClients.delegation.getDelegationContract( + { + params: { delegationId, contractId }, + headers, + } + ); + + const contractBytes = await fileManager.get( + config.delegationContractsContainer, + contract.path, + logger + ); + + return Buffer.from(contractBytes); + }, + async createDelegation( createDelegationBody: bffApi.DelegationSeed, { headers }: WithLogger diff --git a/packages/delegation-process/src/model/domain/errors.ts b/packages/delegation-process/src/model/domain/errors.ts index 72a24fd4a7..6158d82ca8 100644 --- a/packages/delegation-process/src/model/domain/errors.ts +++ b/packages/delegation-process/src/model/domain/errors.ts @@ -5,9 +5,10 @@ import { makeApiProblemBuilder, TenantId, DelegationState, + DelegationId, + DelegationContractId, DelegationKind, Tenant, - DelegationId, } from "pagopa-interop-models"; import { match } from "ts-pattern"; @@ -24,6 +25,7 @@ export const errorCodes = { operationRestrictedToDelegate: "0010", incorrectState: "0011", differentEserviceProducer: "0012", + delegationContractNotFound: "0013", }; export type ErrorCodes = keyof typeof errorCodes; @@ -148,6 +150,17 @@ export function differentEServiceProducer( }); } +export function delegationContractNotFound( + delegationId: DelegationId, + contractId: DelegationContractId +): ApiError { + return new ApiError({ + detail: `Contract ${contractId} of delegation ${delegationId} not found`, + code: "delegationContractNotFound", + title: "Delegation contract not found", + }); +} + export function delegationStampNotFound( stamp: keyof Delegation["stamps"] ): ApiError { diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 82aa33063b..9aed2fe580 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -21,6 +21,7 @@ import { makeApiProblem } from "../model/domain/errors.js"; import { getDelegationsErrorMapper, getDelegationByIdErrorMapper, + getDelegationContractErrorMapper, } from "../utilities/errorMappers.js"; import { delegationServiceBuilder } from "../services/delegationService.js"; @@ -132,6 +133,42 @@ const delegationRouter = ( ctx.correlationId ); + return res.status(errorRes.status).send(errorRes); + } + } + ) + .get( + "/delegations/:delegationId/contracts/:contractId", + authorizationMiddleware([ + ADMIN_ROLE, + API_ROLE, + SECURITY_ROLE, + M2M_ROLE, + SUPPORT_ROLE, + ]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + const { delegationId, contractId } = req.params; + + try { + const contract = await delegationService.getDelegationContract( + unsafeBrandId(delegationId), + unsafeBrandId(contractId), + ctx + ); + + return res + .status(200) + .send(delegationApi.DelegationContractDocument.parse(contract)); + } catch (error) { + const errorRes = makeApiProblem( + error, + getDelegationContractErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving contract ${req.params.contractId} of delegation ${req.params.delegationId}` + ); + return res.status(errorRes.status).send(errorRes); } } diff --git a/packages/delegation-process/src/services/delegationService.ts b/packages/delegation-process/src/services/delegationService.ts index 0b55683fb4..ecd9db121a 100644 --- a/packages/delegation-process/src/services/delegationService.ts +++ b/packages/delegation-process/src/services/delegationService.ts @@ -1,5 +1,7 @@ import { Delegation, + DelegationContractDocument, + DelegationContractId, DelegationId, DelegationKind, DelegationState, @@ -7,9 +9,13 @@ import { TenantId, WithMetadata, } from "pagopa-interop-models"; -import { Logger } from "pagopa-interop-commons"; -import { delegationNotFound } from "../model/domain/errors.js"; +import { AppContext, Logger, WithLogger } from "pagopa-interop-commons"; +import { + delegationNotFound, + delegationContractNotFound, +} from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; +import { assertRequesterIsDelegateOrDelegator } from "./validators.js"; export const retrieveDelegationById = async ( readModelService: ReadModelService, @@ -71,5 +77,35 @@ export function delegationServiceBuilder(readModelService: ReadModelService) { limit, }); }, + async getDelegationContract( + delegationId: DelegationId, + contractId: DelegationContractId, + { logger, authData }: WithLogger + ): Promise { + logger.info( + `Retrieving delegation ${delegationId} contract ${contractId}` + ); + const delegation = await retrieveDelegationById( + readModelService, + delegationId + ); + + assertRequesterIsDelegateOrDelegator( + delegation.data, + authData.organizationId + ); + + const { activationContract, revocationContract } = delegation.data; + + if (contractId === activationContract?.id) { + return activationContract; + } + + if (contractId === revocationContract?.id) { + return revocationContract; + } + + throw delegationContractNotFound(delegationId, contractId); + }, }; } diff --git a/packages/delegation-process/src/services/validators.ts b/packages/delegation-process/src/services/validators.ts index 71ae569c0e..746bb964e8 100644 --- a/packages/delegation-process/src/services/validators.ts +++ b/packages/delegation-process/src/services/validators.ts @@ -7,6 +7,7 @@ import { delegationState, EService, EServiceId, + operationForbidden, PUBLIC_ADMINISTRATIONS_IDENTIFIER, Tenant, TenantId, @@ -135,6 +136,18 @@ export const assertIsState = ( } }; +export const assertRequesterIsDelegateOrDelegator = ( + delegation: Delegation, + requesterId: TenantId +): void => { + if ( + delegation.delegateId !== requesterId && + delegation.delegatorId !== requesterId + ) { + throw operationForbidden; + } +}; + export function assertStampExists( stamps: Delegation["stamps"], stamp: S diff --git a/packages/delegation-process/src/utilities/errorMappers.ts b/packages/delegation-process/src/utilities/errorMappers.ts index 8078c364fd..98c271a653 100644 --- a/packages/delegation-process/src/utilities/errorMappers.ts +++ b/packages/delegation-process/src/utilities/errorMappers.ts @@ -64,3 +64,15 @@ export const approveDelegationErrorMapper = ( .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); export const rejectDelegationErrorMapper = approveDelegationErrorMapper; + +export const getDelegationContractErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with( + "delegationNotFound", + "delegationContractNotFound", + () => HTTP_STATUS_NOT_FOUND + ) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/delegation-process/test/getDelegationContract.test.ts b/packages/delegation-process/test/getDelegationContract.test.ts new file mode 100644 index 0000000000..4a876a4c64 --- /dev/null +++ b/packages/delegation-process/test/getDelegationContract.test.ts @@ -0,0 +1,130 @@ +/* eslint-disable functional/no-let */ +import { + getMockAuthData, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; +import { + DelegationContractDocument, + DelegationContractId, + DelegationId, + delegationKind, + generateId, + operationForbidden, +} from "pagopa-interop-models"; +import { describe, expect, it } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; +import { + delegationContractNotFound, + delegationNotFound, +} from "../src/model/domain/errors.js"; +import { addOneDelegation, delegationService } from "./utils.js"; + +describe("getDelegationContract", () => { + const mockContract: DelegationContractDocument = { + id: generateId(), + contentType: "application/pdf", + createdAt: new Date(), + name: "contract", + path: "path", + prettyName: "prettyName", + }; + + it("should get the delegation contract if it exists", async () => { + const delegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, + }), + activationContract: mockContract, + }; + + await addOneDelegation(delegation); + + const returnedContract = await delegationService.getDelegationContract( + delegation.id, + mockContract.id, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + expect(returnedContract).toEqual(mockContract); + }); + + it("should throw delegationNotFound error if delegation does not exist", async () => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + }); + + await addOneDelegation(delegation); + + const notFoundId = generateId(); + const returnedContract = delegationService.getDelegationContract( + notFoundId, + mockContract.id, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + await expect(returnedContract).rejects.toThrow( + delegationNotFound(notFoundId) + ); + }); + + it("should throw delegationContractNotFound error if the delegation contract does not exist", async () => { + const delegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, + }), + activationContract: mockContract, + }; + + await addOneDelegation(delegation); + + const falseContractId = generateId(); + const returnedContract = delegationService.getDelegationContract( + delegation.id, + falseContractId, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + await expect(returnedContract).rejects.toThrow( + delegationContractNotFound(delegation.id, falseContractId) + ); + }); + + it("should throw operationNotAllowed error if requester is not the delegate nor the delegator", async () => { + const delegation = { + ...getMockDelegation({ + kind: delegationKind.delegatedProducer, + }), + activationContract: mockContract, + }; + + await addOneDelegation(delegation); + + const returnedContract = delegationService.getDelegationContract( + delegation.id, + mockContract.id, + { + authData: getMockAuthData(), + correlationId: generateId(), + logger: genericLogger, + serviceName: "", + } + ); + + await expect(returnedContract).rejects.toThrow(operationForbidden); + }); +}); From b51e4eb96b7b392ce1354997cf99c6cacbc1d8f7 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Mon, 25 Nov 2024 17:39:29 +0100 Subject: [PATCH 042/126] Adding prefix path config to interface exporter (#1232) --- packages/datalake-interface-exporter/.env | 1 + packages/datalake-interface-exporter/.env.test | 1 + packages/datalake-interface-exporter/src/config/config.ts | 2 ++ packages/datalake-interface-exporter/src/interfaceExporter.ts | 2 +- .../test/interfaceExporter.test.ts | 4 ++-- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/datalake-interface-exporter/.env b/packages/datalake-interface-exporter/.env index 91570c3d23..3e87b9cbf6 100644 --- a/packages/datalake-interface-exporter/.env +++ b/packages/datalake-interface-exporter/.env @@ -15,3 +15,4 @@ AWS_CONFIG_FILE=aws.config.local ESERVICE_DOCUMENTS_S3_BUCKET=interop-local-bucket DATALAKE_INTERFACES_EXPORT_S3_BUCKET=interop-data-lake-interfaces-exports-local-es1 +DATALAKE_INTERFACES_EXPORT_PATH=interfaces-export diff --git a/packages/datalake-interface-exporter/.env.test b/packages/datalake-interface-exporter/.env.test index 829c6c2d55..3fbf283885 100644 --- a/packages/datalake-interface-exporter/.env.test +++ b/packages/datalake-interface-exporter/.env.test @@ -10,3 +10,4 @@ S3_BUCKET=test-bucket # We set these two to the names of the two test buckets created by default by the test infra ESERVICE_DOCUMENTS_S3_BUCKET=test-bucket-1 DATALAKE_INTERFACES_EXPORT_S3_BUCKET=test-bucket-2 +DATALAKE_INTERFACES_EXPORT_PATH=interfaces-export diff --git a/packages/datalake-interface-exporter/src/config/config.ts b/packages/datalake-interface-exporter/src/config/config.ts index 54bea1d948..115773bffc 100644 --- a/packages/datalake-interface-exporter/src/config/config.ts +++ b/packages/datalake-interface-exporter/src/config/config.ts @@ -16,11 +16,13 @@ export const DatalakeInterfaceExporterConfig = LoggerConfig.and( .object({ ESERVICE_DOCUMENTS_S3_BUCKET: z.string(), DATALAKE_INTERFACES_EXPORT_S3_BUCKET: z.string(), + DATALAKE_INTERFACES_EXPORT_PATH: z.string(), }) .transform((c) => ({ eserviceDocumentsS3Bucket: c.ESERVICE_DOCUMENTS_S3_BUCKET, datalakeInterfacesExportS3Bucket: c.DATALAKE_INTERFACES_EXPORT_S3_BUCKET, + datalakeInterfacesExportPath: c.DATALAKE_INTERFACES_EXPORT_PATH, })) ); diff --git a/packages/datalake-interface-exporter/src/interfaceExporter.ts b/packages/datalake-interface-exporter/src/interfaceExporter.ts index 9afaae41df..0dbebfaf7b 100644 --- a/packages/datalake-interface-exporter/src/interfaceExporter.ts +++ b/packages/datalake-interface-exporter/src/interfaceExporter.ts @@ -31,7 +31,7 @@ export async function exportInterface( await fileManager.storeBytes( { bucket: config.datalakeInterfacesExportS3Bucket, - path: eserviceId, + path: `${config.datalakeInterfacesExportPath}/${eserviceId}`, resourceId: latestDescriptor.id, name: latestDescriptor.interface.name, content: Buffer.from(interfaceFile), diff --git a/packages/datalake-interface-exporter/test/interfaceExporter.test.ts b/packages/datalake-interface-exporter/test/interfaceExporter.test.ts index 1b466a6f5b..1f1e301dd4 100644 --- a/packages/datalake-interface-exporter/test/interfaceExporter.test.ts +++ b/packages/datalake-interface-exporter/test/interfaceExporter.test.ts @@ -84,7 +84,7 @@ describe("interfaceExporter", () => { genericLogger ) ).toContain( - `${mockEserviceId1}/${mockDescriptor1.id}/${mockDocument1.name}` + `${config.datalakeInterfacesExportPath}/${mockEserviceId1}/${mockDescriptor1.id}/${mockDocument1.name}` ); expect( @@ -93,7 +93,7 @@ describe("interfaceExporter", () => { genericLogger ) ).toContain( - `${mockEserviceId2}/${mockDescriptor2.id}/${mockDocument2.name}` + `${config.datalakeInterfacesExportPath}/${mockEserviceId2}/${mockDescriptor2.id}/${mockDocument2.name}` ); }); From e4f751145b618c86876c6a1d5b52753a84a62830 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Tue, 26 Nov 2024 18:08:36 +0100 Subject: [PATCH 043/126] PIN-5251 - Agreement contract PDF fixes + attributes filter fix (#1153) --- .../src/filters/attributesFilter.ts | 36 +- packages/agreement-lifecycle/src/index.ts | 1 + .../src/utils/verifiedAttributes.ts | 33 ++ packages/agreement-process/package.json | 1 + .../documents/agreementContractTemplate.html | 194 +++++---- .../src/services/agreementContractBuilder.ts | 148 +++---- .../test/activateAgreement.test.ts | 9 +- .../test/agreementStatesFlows.test.ts | 23 +- .../test/computeAgreementsState.test.ts | 3 +- .../test/rejectAgreement.test.ts | 3 +- .../test/submitAgreement.test.ts | 35 +- .../test/upgradeAgreement.test.ts | 9 +- .../test/verifiedAttributes.test.ts | 386 ++++++++++++++++++ .../commons/src/pdf-generator/pdfGenerator.ts | 4 +- packages/models/src/agreement/agreement.ts | 22 +- pnpm-lock.yaml | 3 + 16 files changed, 704 insertions(+), 206 deletions(-) create mode 100644 packages/agreement-lifecycle/src/utils/verifiedAttributes.ts create mode 100644 packages/agreement-process/test/verifiedAttributes.test.ts diff --git a/packages/agreement-lifecycle/src/filters/attributesFilter.ts b/packages/agreement-lifecycle/src/filters/attributesFilter.ts index 8f2983812e..9d28989b88 100644 --- a/packages/agreement-lifecycle/src/filters/attributesFilter.ts +++ b/packages/agreement-lifecycle/src/filters/attributesFilter.ts @@ -5,23 +5,47 @@ import { DeclaredTenantAttribute, TenantAttribute, TenantId, + TenantVerifier, VerifiedTenantAttribute, tenantAttributeType, } from "pagopa-interop-models"; +import { isVerificationRevoked } from "../utils/verifiedAttributes.js"; export const filterVerifiedAttributes = ( - producerId: TenantId, + verifierId: TenantId, tenantAttributes: TenantAttribute[] -): VerifiedTenantAttribute[] => - tenantAttributes.filter( +): VerifiedTenantAttribute[] => { + const now = new Date(); + + const isVerificationExpired = (verification: TenantVerifier): boolean => { + if (verification.extensionDate && verification.expirationDate) { + return ( + verification.extensionDate <= now && verification.expirationDate <= now + ); + } + + if (verification.extensionDate) { + return verification.extensionDate <= now; + } + + if (verification.expirationDate) { + return verification.expirationDate <= now; + } + + return false; + }; + + return tenantAttributes.filter( (att) => att.type === tenantAttributeType.VERIFIED && - att.verifiedBy.find( + att.verifiedBy.some( (v) => - v.id === producerId && - (!v.extensionDate || v.extensionDate > new Date()) + v.id === verifierId && + !isVerificationRevoked(verifierId, att) && + !isVerificationExpired(v) ) ) as VerifiedTenantAttribute[]; +}; export const filterCertifiedAttributes = ( tenantAttributes: TenantAttribute[] diff --git a/packages/agreement-lifecycle/src/index.ts b/packages/agreement-lifecycle/src/index.ts index 7b2d9f8c57..d1209ce887 100644 --- a/packages/agreement-lifecycle/src/index.ts +++ b/packages/agreement-lifecycle/src/index.ts @@ -1,2 +1,3 @@ export * from "./validator/attributesValidator.js"; export * from "./filters/attributesFilter.js"; +export * from "./utils/verifiedAttributes.js"; diff --git a/packages/agreement-lifecycle/src/utils/verifiedAttributes.ts b/packages/agreement-lifecycle/src/utils/verifiedAttributes.ts new file mode 100644 index 0000000000..a41b946308 --- /dev/null +++ b/packages/agreement-lifecycle/src/utils/verifiedAttributes.ts @@ -0,0 +1,33 @@ +import { + TenantId, + TenantVerifier, + VerifiedTenantAttribute, +} from "pagopa-interop-models"; + +export const isVerificationRevoked = ( + verifierId: TenantId, + attribute: VerifiedTenantAttribute +): boolean => + attribute.revokedBy.some((revocation) => revocation.id === verifierId); + +export function getVerifiedAttributeExpirationDate( + verifierId: TenantId, + attribute: VerifiedTenantAttribute +): Date | undefined { + const activeProducerVerification: TenantVerifier | undefined = + attribute.verifiedBy + .filter((verification) => verification.id === verifierId) + .sort( + (a, b) => a.verificationDate.getTime() - b.verificationDate.getTime() + ) + .find( + (verification) => !isVerificationRevoked(verification.id, attribute) + ); + + // We assume that if the extensionDate is defined, it is always + // after the expirationDate, so we return the extensionDate if it's defined + return ( + activeProducerVerification?.extensionDate ?? + activeProducerVerification?.expirationDate + ); +} diff --git a/packages/agreement-process/package.json b/packages/agreement-process/package.json index 4638e17925..d781e57acb 100644 --- a/packages/agreement-process/package.json +++ b/packages/agreement-process/package.json @@ -25,6 +25,7 @@ "@types/express": "4.17.21", "@types/node": "20.14.6", "cpx2": "7.0.1", + "date-fns": "3.6.0", "openapi-zod-client": "1.18.1", "pagopa-interop-commons-test": "workspace:*", "pg-promise": "11.8.0", diff --git a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html index 77259d79f7..fd0c9e5610 100644 --- a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html +++ b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html @@ -1,97 +1,137 @@ + + + + + Richiesta di fruizione + + {{{paged-pdf-polyfill}}} + - - - - - Richiesta di fruizione - - {{{paged-pdf-polyfill}}} - - - - -
{{#if declaredAttributes}} @@ -76,7 +83,8 @@

Attributi dichiarati

requisito di fruizione stabilito dall’erogatore per l’accesso all’e-service.
- {{/each}} {{/if}} {{#if verifiedAttributes}} + {{/each}} {{/if}} + {{#if verifiedAttributes}}

Attributi verificati

@@ -86,15 +94,16 @@

Attributi verificati

{{this.assignmentDate}} alle ore {{this.assignmentTime}}, l’infrastruttura ha registrato - la verifica dell’erogatore che il fruitore possegga l’attributo - verificato {{this.attributeName}}, contraddistinto da - id {{this.attributeId}}, {{#if this.expirationDate}} ed + la verifica {{#if delegationId}}del delegato all’erogazione{{else}}dell’erogatore{{/if}} + che il fruitore possegga l’attributo verificato {{this.attributeName}}, + contraddistinto da id {{this.attributeId}}, {{#if this.expirationDate}} ed avente scadenza il {{this.expirationDate}},{{/if}} necessario a soddisfare il requisito di fruizione stabilito dall’erogatore per l’accesso all’e-service.
- {{/each}} {{/if}} {{#if certifiedAttributes}} + {{/each}} {{/if}} + {{#if certifiedAttributes}}

Attributi certificati

{{#each certifiedAttributes}} @@ -118,7 +127,16 @@

Registrazione

{{submissionTime}}, l’Infrastruttura ha trasmesso all’erogatore la richiesta di fruizione. - + {{#if producerDelegationId}} +
+ In data {{activationDate}} alle ore + {{activationTime}}, l’infrastruttura ha ricevuto + la dichiarazione di accettazione della richiesta di fruizione + (di seguito “dichiarazione di accettazione”) inviata dal delegato all’erogazione, + tramite l’operatore amministrativo con identificativo Area Riservata + {{producerDelegateId}}. +
+ {{else}}
In data {{activationDate}} alle ore {{activationTime}}, l’Infrastruttura ha ricevuto la @@ -127,6 +145,7 @@

Registrazione

l’operatore amministrativo con identificativo Area Riservata {{activatorId}}.
+ {{/if}}
In data {{activationDate}} alle ore diff --git a/packages/agreement-process/src/services/agreementActivationProcessor.ts b/packages/agreement-process/src/services/agreementActivationProcessor.ts index c1ae2129ed..ff33a9d960 100644 --- a/packages/agreement-process/src/services/agreementActivationProcessor.ts +++ b/packages/agreement-process/src/services/agreementActivationProcessor.ts @@ -6,9 +6,11 @@ import { AgreementEventV2, AgreementState, CorrelationId, + DelegationId, Descriptor, EService, Tenant, + TenantId, UserId, agreementState, genericError, @@ -47,6 +49,7 @@ export function createActivationUpdateAgreementSeed({ suspendedByConsumer, suspendedByProducer, suspendedByPlatform, + producerDelegationId, }: { isFirstActivation: boolean; newState: AgreementState; @@ -58,8 +61,9 @@ export function createActivationUpdateAgreementSeed({ suspendedByConsumer: boolean | undefined; suspendedByProducer: boolean | undefined; suspendedByPlatform: boolean | undefined; + producerDelegationId?: DelegationId | undefined; }): UpdateAgreementSeed { - const stamp = createStamp(authData.userId); + const stamp = createStamp(authData.userId, producerDelegationId); return isFirstActivation ? { @@ -113,7 +117,8 @@ export async function createActivationEvent( suspendedByPlatformChanged: boolean, agreementEventStoreVersion: number, authData: AuthData, - correlationId: CorrelationId + correlationId: CorrelationId, + delegateProducerId?: TenantId ): Promise>> { if (isFirstActivation) { // Pending >>> Active @@ -132,7 +137,7 @@ export async function createActivationEvent( /* Not a first activation, meaning that the agreement was already active and it was then suspended. If the requester is the producer (or producer === consumer), the updatedAgreement was updated setting the suspendedByProducer flag to false, - and here we create the unsuspension by producer event. + and here we create the unsuspension by producer event, it works in the same way if requester is delegate producer. Otherwise, the requester is the consumer, and the updatedAgreement was updated setting the suspendedByConsumer flag to false, so we create the unsuspension by consumer event. @@ -150,30 +155,41 @@ export async function createActivationEvent( we also create the corresponding suspension/unsuspension by platform event. */ - return match([authData.organizationId, updatedAgreement.state]) - .with([updatedAgreement.producerId, agreementState.active], () => [ - toCreateEventAgreementUnsuspendedByProducer( - updatedAgreement, - agreementEventStoreVersion, - correlationId - ), - ]) - .with([updatedAgreement.producerId, agreementState.suspended], () => [ - toCreateEventAgreementUnsuspendedByProducer( - { - ...updatedAgreement, - suspendedByPlatform: originalSuspendedByPlatform, - }, - agreementEventStoreVersion, - correlationId - ), - ...maybeCreateSuspensionByPlatformEvents( - updatedAgreement, - suspendedByPlatformChanged, - agreementEventStoreVersion + 1, - correlationId - ), - ]) + return match<[TenantId | undefined, AgreementState]>([ + authData.organizationId, + updatedAgreement.state, + ]) + .with( + [updatedAgreement.producerId, agreementState.active], + [delegateProducerId, agreementState.active], + () => [ + toCreateEventAgreementUnsuspendedByProducer( + updatedAgreement, + agreementEventStoreVersion, + correlationId + ), + ] + ) + .with( + [updatedAgreement.producerId, agreementState.suspended], + [delegateProducerId, agreementState.suspended], + () => [ + toCreateEventAgreementUnsuspendedByProducer( + { + ...updatedAgreement, + suspendedByPlatform: originalSuspendedByPlatform, + }, + agreementEventStoreVersion, + correlationId + ), + ...maybeCreateSuspensionByPlatformEvents( + updatedAgreement, + suspendedByPlatformChanged, + agreementEventStoreVersion + 1, + correlationId + ), + ] + ) .with([updatedAgreement.consumerId, agreementState.active], () => [ toCreateEventAgreementUnsuspendedByConsumer( updatedAgreement, diff --git a/packages/agreement-process/src/services/agreementContractBuilder.ts b/packages/agreement-process/src/services/agreementContractBuilder.ts index 7ecc4c3394..e60d3c461d 100644 --- a/packages/agreement-process/src/services/agreementContractBuilder.ts +++ b/packages/agreement-process/src/services/agreementContractBuilder.ts @@ -24,6 +24,7 @@ import { generateId, tenantAttributeType, AgreementDocument, + Delegation, PUBLIC_ADMINISTRATIONS_IDENTIFIER, } from "pagopa-interop-models"; import { match } from "ts-pattern"; @@ -32,11 +33,17 @@ import { attributeNotFound } from "../model/domain/errors.js"; import { AgreementProcessConfig } from "../config/config.js"; import { assertStampExists } from "../model/domain/agreement-validators.js"; import { ReadModelService } from "./readModelService.js"; -import { retrieveDescriptor } from "./agreementService.js"; +import { retrieveDescriptor, retrieveTenant } from "./agreementService.js"; const CONTENT_TYPE_PDF = "application/pdf"; const AGREEMENT_CONTRACT_PRETTY_NAME = "Richiesta di fruizione"; +export type DelegationData = { + producerDelegation: Delegation; + delegator: Tenant; + delegate: Tenant; +}; + const createAgreementDocumentName = ( consumerId: TenantId, producerId: TenantId, @@ -135,6 +142,7 @@ const getPdfPayload = async ( eservice: EService, consumer: Tenant, producer: Tenant, + producerDelegationData: DelegationData | undefined, readModelService: ReadModelService ): Promise => { const getIpaCode = (tenant: Tenant): string | undefined => @@ -201,6 +209,33 @@ const getPdfPayload = async ( : undefined, }; }), + producerDelegationId: producerDelegationData?.producerDelegation.id, + producerDelegatorName: producerDelegationData?.delegator.name, + producerDelegatorIpaCode: + producerDelegationData && getIpaCode(producerDelegationData?.delegator), + producerDelegateName: producerDelegationData?.delegate.name, + producerDelegateIpaCode: + producerDelegationData && getIpaCode(producerDelegationData?.delegate), + }; +}; + +const buildProducerDelegationData = async ( + producerDelegation: Delegation, + readModelService: ReadModelService +): Promise => { + const delegator = await retrieveTenant( + producerDelegation.delegatorId, + readModelService + ); + const delegate = await retrieveTenant( + producerDelegation.delegateId, + readModelService + ); + + return { + producerDelegation, + delegator, + delegate, }; }; @@ -226,13 +261,22 @@ export const contractBuilder = ( agreement: Agreement, eservice: EService, consumer: Tenant, - producer: Tenant + producer: Tenant, + producerDelegation: Delegation | undefined ): Promise => { + const producerDelegationData = + producerDelegation && + (await buildProducerDelegationData( + producerDelegation, + readModelService + )); + const pdfPayload = await getPdfPayload( agreement, eservice, consumer, producer, + producerDelegationData, readModelService ); diff --git a/packages/agreement-process/src/services/agreementService.ts b/packages/agreement-process/src/services/agreementService.ts index 1643e988a6..a49f88aaca 100644 --- a/packages/agreement-process/src/services/agreementService.ts +++ b/packages/agreement-process/src/services/agreementService.ts @@ -34,6 +34,8 @@ import { unsafeBrandId, CompactTenant, CorrelationId, + Delegation, + delegationKind, } from "pagopa-interop-models"; import { declaredAttributesSatisfied, @@ -84,9 +86,11 @@ import { assertActivableState, assertCanWorkOnConsumerDocuments, assertExpectedState, + assertRequesterCanActivate, + assertRequesterCanSuspend, assertRequesterIsConsumer, - assertRequesterIsConsumerOrProducer, - assertRequesterIsProducer, + assertRequesterIsConsumerOrProducerOrDelegateProducer, + assertRequesterIsProducerOrDelegateProducer, assertSubmittableState, failOnActivationFailure, matchingCertifiedAttributes, @@ -169,6 +173,15 @@ export const retrieveTenant = async ( return tenant; }; +export const retrieveActiveProducerDelegationByEserviceId = async ( + eserviceId: EServiceId, + readModelService: ReadModelService +): Promise => + await readModelService.getActiveDelegationByEserviceId( + eserviceId, + delegationKind.delegatedProducer + ); + export const retrieveDescriptor = ( descriptorId: DescriptorId, eservice: EService @@ -397,10 +410,18 @@ export function agreementServiceBuilder( readModelService ); + const activeProducerDelegation = + await retrieveActiveProducerDelegationByEserviceId( + agreement.data.eserviceId, + readModelService + ); + const delegateProducerId = activeProducerDelegation?.delegateId; + const nextStateByAttributes = nextStateByAttributesFSM( agreement.data, descriptor, - consumer + consumer, + delegateProducerId ); const suspendedByPlatform = suspendedByPlatformFlag( @@ -481,7 +502,8 @@ export function agreementServiceBuilder( eservice, consumer, producer, - updatedAgreement + updatedAgreement, + activeProducerDelegation ); const agreementEvent = @@ -749,7 +771,12 @@ export function agreementServiceBuilder( `Retrieving consumer document ${documentId} from agreement ${agreementId}` ); const agreement = await retrieveAgreement(agreementId, readModelService); - assertRequesterIsConsumerOrProducer(agreement.data, authData); + + await assertRequesterIsConsumerOrProducerOrDelegateProducer( + agreement.data, + authData, + readModelService + ); return retrieveAgreementDocument(agreement.data, documentId); }, @@ -760,8 +787,15 @@ export function agreementServiceBuilder( logger.info(`Suspending agreement ${agreementId}`); const agreement = await retrieveAgreement(agreementId, readModelService); + const activeProducerDelegation = + await retrieveActiveProducerDelegationByEserviceId( + agreement.data.eserviceId, + readModelService + ); - assertRequesterIsConsumerOrProducer(agreement.data, authData); + const delegateProducerId = activeProducerDelegation?.delegateId; + + assertRequesterCanSuspend(agreement.data, delegateProducerId, authData); assertExpectedState( agreementId, @@ -789,6 +823,7 @@ export function agreementServiceBuilder( authData, descriptor, consumer, + producerDelegation: activeProducerDelegation, }); await repository.createEvent( @@ -796,7 +831,8 @@ export function agreementServiceBuilder( authData.organizationId, correlationId, updatedAgreement, - agreement + agreement, + delegateProducerId ) ); @@ -865,8 +901,18 @@ export function agreementServiceBuilder( agreementId, readModelService ); + const activeProducerDelegation = + await retrieveActiveProducerDelegationByEserviceId( + agreementToBeRejected.data.eserviceId, + readModelService + ); + const delegateProducerId = activeProducerDelegation?.delegateId; - assertRequesterIsProducer(agreementToBeRejected.data, authData); + assertRequesterIsProducerOrDelegateProducer( + agreementToBeRejected.data, + delegateProducerId, + authData + ); assertExpectedState( agreementId, @@ -905,7 +951,7 @@ export function agreementServiceBuilder( suspendedByPlatform: undefined, stamps: { ...agreementToBeRejected.data.stamps, - rejection: createStamp(authData.userId), + rejection: createStamp(authData.userId, activeProducerDelegation?.id), }, }; @@ -933,8 +979,16 @@ export function agreementServiceBuilder( ); const agreement = await retrieveAgreement(agreementId, readModelService); + const activeProducerDelegation = + await retrieveActiveProducerDelegationByEserviceId( + agreement.data.eserviceId, + readModelService + ); + + const delegateProducerId = activeProducerDelegation?.delegateId; + + assertRequesterCanActivate(agreement.data, delegateProducerId, authData); - assertRequesterIsConsumerOrProducer(agreement.data, authData); verifyConsumerDoesNotActivatePending(agreement.data, authData); assertActivableState(agreement.data); @@ -1026,6 +1080,7 @@ export function agreementServiceBuilder( suspendedByConsumer, suspendedByProducer, suspendedByPlatform, + producerDelegationId: activeProducerDelegation?.id, }); const updatedAgreementWithoutContract: Agreement = { @@ -1039,7 +1094,8 @@ export function agreementServiceBuilder( eservice, consumer, producer, - updatedAgreementWithoutContract + updatedAgreementWithoutContract, + activeProducerDelegation ); const suspendedByPlatformChanged = @@ -1053,7 +1109,8 @@ export function agreementServiceBuilder( suspendedByPlatformChanged, agreement.metadata.version, authData, - correlationId + correlationId, + delegateProducerId ); const archiveEvents = await archiveRelatedToAgreements( @@ -1220,14 +1277,16 @@ async function addContractOnFirstActivation( eservice: EService, consumer: Tenant, producer: Tenant, - agreement: Agreement + agreement: Agreement, + producerDelegation: Delegation | undefined ): Promise { if (isFirstActivation) { const contract = await contractBuilder.createContract( agreement, eservice, consumer, - producer + producer, + producerDelegation ); return { diff --git a/packages/agreement-process/src/services/agreementStampUtils.ts b/packages/agreement-process/src/services/agreementStampUtils.ts index 793d89ac0b..5505316e02 100644 --- a/packages/agreement-process/src/services/agreementStampUtils.ts +++ b/packages/agreement-process/src/services/agreementStampUtils.ts @@ -2,6 +2,7 @@ import { Agreement, AgreementStamp, AgreementState, + DelegationId, TenantId, UserId, agreementState, @@ -9,8 +10,12 @@ import { } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; -export const createStamp = (userId: UserId): AgreementStamp => ({ +export const createStamp = ( + userId: UserId, + delegationId?: DelegationId | undefined +): AgreementStamp => ({ who: unsafeBrandId(userId), + delegationId, when: new Date(), }); @@ -29,9 +34,21 @@ export const suspendedByProducerStamp = ( agreement: Agreement, requesterOrgId: TenantId, destinationState: AgreementState, - stamp: AgreementStamp + stamp: AgreementStamp, + delegateProducerId?: TenantId | undefined ): AgreementStamp | undefined => - match([requesterOrgId, destinationState]) - .with([agreement.producerId, agreementState.suspended], () => stamp) - .with([agreement.producerId, P.any], () => undefined) + match<[TenantId | undefined, AgreementState]>([ + requesterOrgId, + destinationState, + ]) + .with( + [agreement.producerId, agreementState.suspended], + [delegateProducerId, agreementState.suspended], + () => stamp + ) + .with( + [agreement.producerId, P.any], + [delegateProducerId, P.any], + () => undefined + ) .otherwise(() => agreement.stamps.suspensionByProducer); diff --git a/packages/agreement-process/src/services/agreementStateProcessor.ts b/packages/agreement-process/src/services/agreementStateProcessor.ts index d9e72b76cc..07d43ad7e2 100644 --- a/packages/agreement-process/src/services/agreementStateProcessor.ts +++ b/packages/agreement-process/src/services/agreementStateProcessor.ts @@ -12,6 +12,7 @@ import { agreementState, CompactTenant, CorrelationId, + TenantId, } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { @@ -41,9 +42,13 @@ const { const nextStateFromDraft = ( agreement: Agreement, descriptor: Descriptor, - tenant: Tenant | CompactTenant + tenant: Tenant | CompactTenant, + delegateProducerId: TenantId | undefined ): AgreementState => { - if (agreement.consumerId === agreement.producerId) { + if ( + agreement.consumerId === agreement.producerId || + agreement.consumerId === delegateProducerId + ) { return active; } if (!certifiedAttributesSatisfied(descriptor.attributes, tenant.attributes)) { @@ -93,9 +98,13 @@ const nextStateFromPending = ( const nextStateFromActiveOrSuspended = ( agreement: Agreement, descriptor: Descriptor, - tenant: Tenant | CompactTenant + tenant: Tenant | CompactTenant, + delegateProducerId: TenantId | undefined ): AgreementState => { - if (agreement.consumerId === agreement.producerId) { + if ( + agreement.consumerId === agreement.producerId || + agreement.consumerId === delegateProducerId + ) { return active; } if ( @@ -125,17 +134,23 @@ const nextStateFromMissingCertifiedAttributes = ( export const nextStateByAttributesFSM = ( agreement: Agreement, descriptor: Descriptor, - tenant: Tenant | CompactTenant + tenant: Tenant | CompactTenant, + delegateProducerId?: TenantId | undefined ): AgreementState => match(agreement.state) .with(agreementState.draft, () => - nextStateFromDraft(agreement, descriptor, tenant) + nextStateFromDraft(agreement, descriptor, tenant, delegateProducerId) ) .with(agreementState.pending, () => nextStateFromPending(agreement, descriptor, tenant) ) .with(agreementState.active, agreementState.suspended, () => - nextStateFromActiveOrSuspended(agreement, descriptor, tenant) + nextStateFromActiveOrSuspended( + agreement, + descriptor, + tenant, + delegateProducerId + ) ) .with(agreementState.archived, () => archived) .with(agreementState.missingCertifiedAttributes, () => @@ -182,9 +197,11 @@ export const suspendedByConsumerFlag = ( export const suspendedByProducerFlag = ( agreement: Agreement, requesterOrgId: Tenant["id"], - targetDestinationState: AgreementState + targetDestinationState: AgreementState, + delegateProducerId?: TenantId | undefined ): boolean | undefined => - requesterOrgId === agreement.producerId + requesterOrgId === agreement.producerId || + requesterOrgId === delegateProducerId ? targetDestinationState === agreementState.suspended : agreement.suspendedByProducer; diff --git a/packages/agreement-process/src/services/agreementSuspensionProcessor.ts b/packages/agreement-process/src/services/agreementSuspensionProcessor.ts index b7a1550f89..504ed9bc50 100644 --- a/packages/agreement-process/src/services/agreementSuspensionProcessor.ts +++ b/packages/agreement-process/src/services/agreementSuspensionProcessor.ts @@ -3,6 +3,7 @@ import { Agreement, AgreementEventV2, CorrelationId, + Delegation, Descriptor, Tenant, TenantId, @@ -32,11 +33,13 @@ export function createSuspensionUpdatedAgreement({ authData, descriptor, consumer, + producerDelegation, }: { agreement: Agreement; authData: AuthData; descriptor: Descriptor; consumer: Tenant; + producerDelegation: Delegation | undefined; }): Agreement { /* nextAttributesState VS targetDestinationState -- targetDestinationState is the state where the caller wants to go (suspended, in this case) @@ -46,7 +49,8 @@ export function createSuspensionUpdatedAgreement({ const nextStateByAttributes = nextStateByAttributesFSM( agreement, descriptor, - consumer + consumer, + producerDelegation?.delegateId ); const suspendedByConsumer = suspendedByConsumerFlag( @@ -57,7 +61,8 @@ export function createSuspensionUpdatedAgreement({ const suspendedByProducer = suspendedByProducerFlag( agreement, authData.organizationId, - targetDestinationState + targetDestinationState, + producerDelegation?.delegateId ); const newState = agreementStateByFlags( @@ -67,13 +72,14 @@ export function createSuspensionUpdatedAgreement({ agreement.suspendedByPlatform ); - const stamp = createStamp(authData.userId); + const stamp = createStamp(authData.userId, producerDelegation?.id); const suspensionByProducerStamp = suspendedByProducerStamp( agreement, authData.organizationId, agreementState.suspended, - stamp + stamp, + producerDelegation?.delegateId ); const suspensionByConsumerStamp = suspendedByConsumerStamp( @@ -105,18 +111,20 @@ export function createAgreementSuspendedEvent( organizationId: TenantId, correlationId: CorrelationId, updatedAgreement: Agreement, - agreement: WithMetadata + agreement: WithMetadata, + delegateProducerId: TenantId | undefined ): CreateEvent { const isProducer = organizationId === agreement.data.producerId; const isConsumer = organizationId === agreement.data.consumerId; + const isProducerDelegate = delegateProducerId === organizationId; - if (!isProducer && !isConsumer) { + if (!isProducer && !isConsumer && !isProducerDelegate) { throw genericError( - "Agreement can only be suspended by the consumer or producer." + "Agreement can only be suspended by the consumer or producer/delegate producer." ); } - return isProducer + return isProducer || isProducerDelegate ? toCreateEventAgreementSuspendedByProducer( updatedAgreement, agreement.metadata.version, diff --git a/packages/agreement-process/src/services/agreementUpgradeProcessor.ts b/packages/agreement-process/src/services/agreementUpgradeProcessor.ts index b3a2332cd8..3066cb7c06 100644 --- a/packages/agreement-process/src/services/agreementUpgradeProcessor.ts +++ b/packages/agreement-process/src/services/agreementUpgradeProcessor.ts @@ -27,7 +27,10 @@ import { toCreateEventAgreementArchivedByUpgrade, toCreateEventAgreementUpgraded, } from "../model/domain/toEvent.js"; -import { createAndCopyDocumentsForClonedAgreement } from "./agreementService.js"; +import { + createAndCopyDocumentsForClonedAgreement, + retrieveActiveProducerDelegationByEserviceId, +} from "./agreementService.js"; import { createStamp } from "./agreementStampUtils.js"; import { ReadModelService } from "./readModelService.js"; import { ContractBuilder } from "./agreementContractBuilder.js"; @@ -65,7 +68,18 @@ export async function createUpgradeOrNewDraft({ // Creates a new Agreement linked to the new descriptor version, // with the same state of the old agreement, and archives the old agreement. - const stamp = createStamp(authData.userId); + // If current eservice has an active producer delegation the new contract will be created with the delegation data + const activeProducerDelegation = + await retrieveActiveProducerDelegationByEserviceId( + eservice.id, + readModelService + ); + + const stamp = + authData.organizationId === activeProducerDelegation?.delegateId + ? createStamp(authData.userId, activeProducerDelegation?.id) + : createStamp(authData.userId); + const archived: Agreement = { ...agreement.data, state: agreementState.archived, @@ -113,7 +127,8 @@ export async function createUpgradeOrNewDraft({ upgraded, eservice, consumer, - producer + producer, + activeProducerDelegation ); const upgradedWithContract: Agreement = { diff --git a/packages/agreement-process/src/services/readModelService.ts b/packages/agreement-process/src/services/readModelService.ts index 23b7975c30..c37f51fd97 100644 --- a/packages/agreement-process/src/services/readModelService.ts +++ b/packages/agreement-process/src/services/readModelService.ts @@ -25,6 +25,14 @@ import { AttributeReadmodel, TenantId, genericInternalError, + DelegationState, + Delegation, + delegationState, + AgreementReadModel, + DescriptorReadModel, + EServiceReadModel, + DelegationKind, + delegationKind, } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; import { z } from "zod"; @@ -289,106 +297,138 @@ async function searchTenantsByName( } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export function readModelServiceBuilder( - readModelRepository: ReadModelRepository -) { - const agreements = readModelRepository.agreements; - const eservices = readModelRepository.eservices; - const tenants = readModelRepository.tenants; - const attributes = readModelRepository.attributes; - return { - async getAgreements( - filters: AgreementQueryFilters, - limit: number, - offset: number - ): Promise> { - const aggregationPipeline = [ - getAgreementsFilters(filters), +function getDelegateAgreementsFilters(producerIds: TenantId[] | undefined) { + return producerIds && producerIds.length > 0 + ? [ { $lookup: { - from: "eservices", + from: "delegations", localField: "data.eserviceId", - foreignField: "data.id", - as: "eservices", + foreignField: "data.eserviceId", + as: "delegations", }, }, { - $unwind: "$eservices", + $unwind: { + path: "$delegations", + preserveNullAndEmptyArrays: true, + }, }, - ...(filters.showOnlyUpgradeable - ? [ + { + $match: { + $or: [ { - $addFields: { - currentDescriptor: { - $filter: { - input: "$eservices.data.descriptors", - as: "descr", - cond: { - $eq: ["$$descr.id", "$data.descriptorId"], - }, - }, + $and: [ + { + "delegations.data.kind": delegationKind.delegatedProducer, }, - }, - }, - { - $unwind: "$currentDescriptor", - }, - { - $addFields: { - upgradableDescriptor: { - $filter: { - input: "$eservices.data.descriptors", - as: "upgradable", - cond: { - $and: [ - { - $gt: [ - "$$upgradable.publishedAt", - "$currentDescriptor.publishedAt", - ], - }, - { - $in: [ - "$$upgradable.state", - [ - descriptorState.published, - descriptorState.suspended, - ], - ], - }, - ], - }, + { + "delegations.data.state": agreementState.active, + }, + { + "delegations.data.delegateId": { + $in: producerIds, }, }, - }, + ], }, { - $match: { - upgradableDescriptor: { $ne: [] }, + "data.producerId": { + $in: producerIds, }, }, - ] - : []), - { - $project: { - data: 1, - eservices: 1, - lowerName: { $toLower: "$eservices.data.name" }, + ], }, }, - { - $sort: { lowerName: 1 }, - }, - ]; + ] + : []; +} - const data = await agreements +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function readModelServiceBuilder( + readModelRepository: ReadModelRepository +) { + const agreements = readModelRepository.agreements; + const eservices = readModelRepository.eservices; + const tenants = readModelRepository.tenants; + const attributes = readModelRepository.attributes; + const delegations = readModelRepository.delegations; + return { + async getAgreements( + filters: AgreementQueryFilters, + limit: number, + offset: number + ): Promise> { + const { producerId, ...filtersWithoutProducerId } = filters; + const producerIds = producerId + ? Array.isArray(producerId) + ? producerId + : [producerId] + : []; + + const agreementsData = await agreements .aggregate( - [...aggregationPipeline, { $skip: offset }, { $limit: limit }], - { allowDiskUse: true } + [ + getAgreementsFilters(filtersWithoutProducerId), + ...getDelegateAgreementsFilters(producerIds), + ], + { + allowDiskUse: true, + } ) .toArray(); - const result = z.array(Agreement).safeParse(data.map((d) => d.data)); + const eserviceIds = agreementsData.map( + (agreement) => agreement.data.eserviceId + ); + const eservicesData = await eservices + .find({ "data.id": { $in: eserviceIds } }) + .toArray(); + + const eservicesMap = new Map( + eservicesData.map((eservice) => [eservice.data.id, eservice.data]) + ); + + const combinedData: Array<{ + agreement: AgreementReadModel; + eservice: EServiceReadModel; + }> = agreementsData.flatMap((agreement) => { + const eservice = eservicesMap.get(agreement.data.eserviceId); + return eservice ? [{ agreement: agreement.data, eservice }] : []; + }); + + const filteredData = filters.showOnlyUpgradeable + ? combinedData.filter((cb) => { + const currentDescriptor = cb.eservice.descriptors.find( + (descr) => descr.id === cb.agreement.descriptorId + ); + const upgradableDescriptor = cb.eservice.descriptors.filter( + (upgradable: DescriptorReadModel) => { + // Since the dates are optional, if they are undefined they are set to a very old date + const currentPublishedAt = + currentDescriptor?.publishedAt ?? new Date(0); + const upgradablePublishedAt = + upgradable.publishedAt ?? new Date(0); + return ( + upgradablePublishedAt > currentPublishedAt && + (upgradable.state === descriptorState.published || + upgradable.state === descriptorState.suspended) + ); + } + ); + return upgradableDescriptor.length > 0; + }) + : combinedData; + + const data = filteredData + .slice(offset, offset + limit) + .sort((a, b) => + a.eservice.name + .toLowerCase() + .localeCompare(b.eservice.name.toLowerCase()) + ); + + const result = z.array(Agreement).safeParse(data.map((d) => d.agreement)); if (!result.success) { throw genericInternalError( `Unable to parse agreements items: result ${JSON.stringify( @@ -399,10 +439,7 @@ export function readModelServiceBuilder( return { results: result.data, - totalCount: await ReadModelRepository.getTotalCount( - agreements, - aggregationPipeline - ), + totalCount: filteredData.length, }; }, async getAgreementById( @@ -505,18 +542,34 @@ export function readModelServiceBuilder( ...(filters.consumerIds.length === 0 ? undefined : { "data.consumerId": { $in: filters.consumerIds } }), - ...(filters.producerIds.length === 0 - ? undefined - : { "data.producerId": { $in: filters.producerIds } }), ...(filters.agreeementStates.length === 0 ? undefined : { "data.state": { $in: filters.agreeementStates } }), }; - const agreementEservicesIds = await agreements.distinct( - "data.eserviceId", - agreementFilter - ); + const agreementAggregationPipeline = [ + ...getDelegateAgreementsFilters(filters.producerIds), + { + $match: agreementFilter, + }, + { + $group: { + _id: "$data.eserviceId", + }, + }, + { + $project: { + _id: 0, + eserviceId: "$_id", + }, + }, + ]; + + const agreementData = await agreements + .aggregate([...agreementAggregationPipeline], { allowDiskUse: true }) + .toArray(); + + const agreementEservicesIds = agreementData.map((d) => d.eserviceId); const aggregationPipeline = [ { @@ -562,6 +615,59 @@ export function readModelServiceBuilder( ), }; }, + async getDelegationByDelegateId( + delegateId: TenantId, + kind: DelegationKind, + state: DelegationState = delegationState.active + ): Promise { + const data = await delegations.findOne( + { + "data.delegateId": delegateId, + "data.state": state, + "data.kind": kind, + }, + { projection: { data: true } } + ); + + if (!data) { + return undefined; + } + const result = z.object({ data: Delegation }).safeParse(data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return result.data.data; + }, + async getActiveDelegationByEserviceId( + eserviceId: EServiceId, + kind: DelegationKind + ): Promise { + const data = await delegations.findOne( + { + "data.eserviceId": eserviceId, + "data.state": delegationState.active, + "data.kind": kind, + }, + { projection: { data: true } } + ); + + if (!data) { + return undefined; + } + const result = z.object({ data: Delegation }).safeParse(data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return result.data.data; + }, }; } diff --git a/packages/agreement-process/test/activateAgreement.test.ts b/packages/agreement-process/test/activateAgreement.test.ts index ad5d4ccec7..9a3b976b87 100644 --- a/packages/agreement-process/test/activateAgreement.test.ts +++ b/packages/agreement-process/test/activateAgreement.test.ts @@ -16,6 +16,7 @@ import { getMockAttribute, getMockCertifiedTenantAttribute, getMockDeclaredTenantAttribute, + getMockDelegation, getMockDescriptorPublished, getMockEService, getMockEServiceAttribute, @@ -28,6 +29,7 @@ import { import { Agreement, AgreementActivatedV2, + AgreementContractPDFPayload, AgreementId, AgreementSetMissingCertifiedAttributesByPlatformV2, AgreementSuspendedByPlatformV2, @@ -39,11 +41,15 @@ import { DeclaredTenantAttribute, Descriptor, EService, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, Tenant, TenantAttribute, TenantId, VerifiedTenantAttribute, agreementState, + attributeKind, + delegationKind, + delegationState, descriptorState, fromAgreementV2, generateId, @@ -71,6 +77,7 @@ import { config } from "../src/config/config.js"; import { addOneAgreement, addOneAttribute, + addOneDelegation, addOneEService, addOneTenant, agreementService, @@ -633,6 +640,310 @@ describe("activate agreement", () => { }) ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); }); + + it("should succeed when the requester is the Delegate and first activation", async () => { + const consumerId = generateId(); + const producerId = generateId(); + const verifiedTenantAttributes = [ + { + ...getMockVerifiedTenantAttribute(), + verifiedBy: [ + { + id: producerId, + verificationDate: new Date(), + extensionDate: undefined, + }, + ], + revokedBy: [], + }, + ]; + const certifiedTenantAttributes = [ + { + ...getMockCertifiedTenantAttribute(), + revocationTimestamp: undefined, + }, + ]; + const declaredTenantAttributes = [ + { + ...getMockDeclaredTenantAttribute(), + revocationTimestamp: undefined, + }, + ]; + + const verifiedAttribute: Attribute = getMockAttribute( + attributeKind.verified, + verifiedTenantAttributes[0].id + ); + + const certifiedAttribute: Attribute = getMockAttribute( + attributeKind.certified, + certifiedTenantAttributes[0].id + ); + + const declaredAttribute: Attribute = getMockAttribute( + attributeKind.declared, + declaredTenantAttributes[0].id + ); + + const producer = getMockTenant(producerId); + const consumer = { + ...getMockTenant(consumerId), + attributes: [ + verifiedTenantAttributes[0], + certifiedTenantAttributes[0], + declaredTenantAttributes[0], + ], + }; + const authData = getRandomAuthData(); + + const descriptor = { + ...getMockDescriptorPublished(), + attributes: { + certified: [ + [getMockEServiceAttribute(certifiedTenantAttributes[0].id)], + ], + declared: [ + [getMockEServiceAttribute(declaredTenantAttributes[0].id)], + ], + verified: [ + [getMockEServiceAttribute(verifiedTenantAttributes[0].id)], + ], + }, + }; + const eservice = { + ...getMockEService(), + producerId: producer.id, + consumerId: consumer.id, + descriptors: [descriptor], + }; + const agreementSubmissionDate = new Date( + new Date().getFullYear(), + new Date().getMonth() - 1, + 1 + ); + + const submitterId = authData.userId; + const activatorId = authData.userId; + const agreement: Agreement = { + ...getMockAgreement(eservice.id), + state: agreementState.pending, + descriptorId: eservice.descriptors[0].id, + producerId: producer.id, + consumerId: consumer.id, + suspendedAt: undefined, + suspendedByConsumer: false, + suspendedByProducer: false, + suspendedByPlatform: false, + verifiedAttributes: [ + ...verifiedTenantAttributes.map(({ id }) => ({ + id, + })), + ], + certifiedAttributes: certifiedTenantAttributes.map(({ id }) => ({ + id, + })), + declaredAttributes: declaredTenantAttributes.map(({ id }) => ({ + id, + })), + stamps: { + submission: { + who: submitterId, + when: agreementSubmissionDate, + }, + }, + }; + + const delegator = getMockTenant(producer.id); + const delegate = getMockTenant(authData.organizationId); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: agreement.eserviceId, + delegateId: delegate.id, + delegatorId: delegator.id, + state: delegationState.active, + }); + + await addOneAttribute(verifiedAttribute); + await addOneAttribute(certifiedAttribute); + await addOneAttribute(declaredAttribute); + await addOneTenant(consumer); + await addOneTenant(producer); + await addOneTenant(delegator); + await addOneTenant(delegate); + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOneDelegation(delegation); + + vi.spyOn(pdfGenerator, "generate"); + const actualAgreement = await agreementService.activateAgreement( + agreement.id, + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); + + const expectedAgreement = { + ...agreement, + state: agreementState.active, + contract: actualAgreement.contract, + certifiedAttributes: actualAgreement.certifiedAttributes, + declaredAttributes: actualAgreement.declaredAttributes, + verifiedAttributes: actualAgreement.verifiedAttributes, + stamps: { + ...agreement.stamps, + activation: { + who: authData.userId, + when: expect.any(Date), + delegationId: delegation.id, + }, + }, + }; + + expect(actualAgreement).toEqual(expectedAgreement); + + // ============================ + // Verify agreement Document + // ============================ + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(actualAgreement.contract?.path); + + expect(submitterId).toEqual(actualAgreement.stamps.submission?.who); + expect(activatorId).toEqual(actualAgreement.stamps.activation?.who); + + const getIpaCode = (tenant: Tenant): string | undefined => + tenant.externalId.origin === PUBLIC_ADMINISTRATIONS_IDENTIFIER + ? tenant.externalId.value + : undefined; + + const expectedAgreementPDFPayload: AgreementContractPDFPayload = { + todayDate: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + todayTime: expect.stringMatching(/^\d{2}:\d{2}:\d{2}$/), + agreementId: agreement.id, + submitterId, + submissionDate: dateAtRomeZone(agreementSubmissionDate), + submissionTime: timeAtRomeZone(agreementSubmissionDate), + activatorId, + activationDate: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + activationTime: expect.stringMatching(/^\d{2}:\d{2}:\d{2}$/), + eserviceName: eservice.name, + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + descriptorVersion: eservice.descriptors[0].version, + producerName: producer.name, + producerIpaCode: getIpaCode(producer), + consumerName: consumer.name, + consumerIpaCode: getIpaCode(consumer), + certifiedAttributes: [ + { + assignmentDate: dateAtRomeZone( + certifiedTenantAttributes[0].assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + certifiedTenantAttributes[0].assignmentTimestamp + ), + attributeName: certifiedAttribute.name, + attributeId: certifiedTenantAttributes[0].id, + }, + ], + declaredAttributes: [ + { + assignmentDate: dateAtRomeZone( + declaredTenantAttributes[0].assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + declaredTenantAttributes[0].assignmentTimestamp + ), + attributeName: declaredAttribute.name, + attributeId: declaredTenantAttributes[0].id, + }, + ], + verifiedAttributes: [ + { + assignmentDate: dateAtRomeZone( + verifiedTenantAttributes[0].assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + verifiedTenantAttributes[0].assignmentTimestamp + ), + attributeName: verifiedAttribute.name, + attributeId: verifiedTenantAttributes[0].id, + expirationDate: undefined, + }, + ], + producerDelegationId: delegation.id, + producerDelegatorName: producer.name, + producerDelegatorIpaCode: getIpaCode(producer), + producerDelegateName: delegate.name, + producerDelegateIpaCode: getIpaCode(delegate), + }; + + expect(pdfGenerator.generate).toHaveBeenCalledWith( + expect.any(String), + expectedAgreementPDFPayload + ); + }); + + it("should succed when the requester is the Delegate and from Suspended", async () => { + const producer = getMockTenant(); + const consumer = getMockTenant(); + const authData = getRandomAuthData(); + const esevice = { + ...getMockEService(), + producerId: producer.id, + consumerId: consumer.id, + descriptors: [getMockDescriptorPublished()], + }; + const agreement: Agreement = { + ...getMockAgreement(esevice.id), + state: agreementState.suspended, + descriptorId: esevice.descriptors[0].id, + producerId: producer.id, + consumerId: consumer.id, + suspendedAt: new Date(), + suspendedByConsumer: false, + suspendedByProducer: false, + suspendedByPlatform: false, + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: agreement.eserviceId, + delegateId: authData.organizationId, + state: delegationState.active, + }); + + await addOneTenant(consumer); + await addOneTenant(producer); + await addOneEService(esevice); + await addOneAgreement(agreement); + await addOneDelegation(delegation); + + const actualAgreement = await agreementService.activateAgreement( + agreement.id, + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); + + const expectedAgreement = { + ...agreement, + state: agreementState.active, + contract: actualAgreement.contract, + certifiedAttributes: actualAgreement.certifiedAttributes, + declaredAttributes: actualAgreement.declaredAttributes, + verifiedAttributes: actualAgreement.verifiedAttributes, + suspendedAt: undefined, + }; + + expect(actualAgreement).toEqual(expectedAgreement); + }); }); describe("Agreement Suspended", () => { @@ -1500,6 +1811,33 @@ describe("activate agreement", () => { ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); }); + it("should throw an operationNotAllowed error when the requester is the Producer but it is not the delegate", async () => { + const authData = getRandomAuthData(); + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.pending, + producerId: authData.organizationId, + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: agreement.eserviceId, + state: delegationState.active, + }); + + const eservice = getMockEService(agreement.eserviceId); + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOneDelegation(delegation); + await expect( + agreementService.activateAgreement(agreement.id, { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); + }); + it.each( Object.values(agreementState).filter( (state) => !agreementActivableStates.includes(state) diff --git a/packages/agreement-process/test/agreementConsumerDocuments.test.ts b/packages/agreement-process/test/agreementConsumerDocuments.test.ts index 55591e916a..cfc060de1d 100644 --- a/packages/agreement-process/test/agreementConsumerDocuments.test.ts +++ b/packages/agreement-process/test/agreementConsumerDocuments.test.ts @@ -4,6 +4,9 @@ import { fileManagerDeleteError, genericLogger } from "pagopa-interop-commons"; import { decodeProtobufPayload, getMockAgreement, + getMockDelegation, + getMockEService, + getMockTenant, getRandomAuthData, randomArrayItem, } from "pagopa-interop-commons-test/index.js"; @@ -17,6 +20,8 @@ import { EServiceId, TenantId, agreementState, + delegationKind, + delegationState, generateId, toAgreementV2, } from "pagopa-interop-models"; @@ -32,6 +37,9 @@ import { import { config } from "../src/config/config.js"; import { addOneAgreement, + addOneDelegation, + addOneEService, + addOneTenant, agreementService, fileManager, getMockConsumerDocument, @@ -73,6 +81,40 @@ describe("agreement consumer document", () => { expect(result).toEqual(agreement1.consumerDocuments[0]); }); + it("should succed when the requester is the delegate", async () => { + const eservice = getMockEService(); + const agreement = { + ...getMockAgreement(eservice.id), + consumerDocuments: [generateMock(AgreementDocument)], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: eservice.producerId, + eserviceId: eservice.id, + state: delegationState.active, + }); + const delegate = getMockTenant(delegation.delegateId); + + await addOneTenant(delegate); + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOneDelegation(delegation); + + const authData = getRandomAuthData(eservice.producerId); + const result = await agreementService.getAgreementConsumerDocument( + agreement.id, + agreement.consumerDocuments[0].id, + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); + + expect(result).toEqual(agreement.consumerDocuments[0]); + }); + it("should throw an agreementNotFound error when the agreement does not exist", async () => { const agreementId = generateId(); const authData = getRandomAuthData(agreement1.consumerId); diff --git a/packages/agreement-process/test/archiveAgreement.test.ts b/packages/agreement-process/test/archiveAgreement.test.ts index b62a85f4cb..faa70aa28b 100644 --- a/packages/agreement-process/test/archiveAgreement.test.ts +++ b/packages/agreement-process/test/archiveAgreement.test.ts @@ -99,7 +99,7 @@ describe("archive agreement", () => { toAgreementV2(expectedAgreemenentArchived) ); - expect(actualAgreement).toMatchObject(toAgreementV2(returnedAgreement)); + expect(actualAgreement).toEqual(toAgreementV2(returnedAgreement)); vi.useRealTimers(); }); diff --git a/packages/agreement-process/test/createAgreement.test.ts b/packages/agreement-process/test/createAgreement.test.ts index 05d8d75151..49f100d1ba 100644 --- a/packages/agreement-process/test/createAgreement.test.ts +++ b/packages/agreement-process/test/createAgreement.test.ts @@ -417,7 +417,9 @@ describe("create agreement", () => { const authData = getRandomAuthData(); const eserviceId = generateId(); const notDraftDescriptorStates = Object.values(descriptorState).filter( - (state) => state !== descriptorState.draft + (state) => + state !== descriptorState.draft && + state !== descriptorState.waitingForApproval ); const descriptor0: Descriptor = { @@ -469,7 +471,8 @@ describe("create agreement", () => { Object.values(descriptorState).filter( (state) => state !== descriptorState.published && - state !== descriptorState.draft + state !== descriptorState.draft && + state !== descriptorState.waitingForApproval ) ), }; diff --git a/packages/agreement-process/test/getAgreementEservices.test.ts b/packages/agreement-process/test/getAgreementEservices.test.ts index 9ba4e0bc9f..5208f1af06 100644 --- a/packages/agreement-process/test/getAgreementEservices.test.ts +++ b/packages/agreement-process/test/getAgreementEservices.test.ts @@ -25,6 +25,7 @@ describe("get agreement eservices", () => { let eservice1: EService; let eservice2: EService; let eservice3: EService; + let eservice4: EService; let tenant1: Tenant; let tenant2: Tenant; @@ -52,6 +53,10 @@ describe("get agreement eservices", () => { ...getMockEService(generateId(), tenant3.id), name: "EService 3 FooBar", }; + eservice4 = { + ...getMockEService(generateId(), tenant3.id), + name: "EService 4 FooBar", + }; await addOneTenant(tenant1); await addOneTenant(tenant2); @@ -59,6 +64,7 @@ describe("get agreement eservices", () => { await addOneEService(eservice1); await addOneEService(eservice2); await addOneEService(eservice3); + await addOneEService(eservice4); const agreement1 = { ...getMockAgreement(eservice1.id), @@ -79,10 +85,17 @@ describe("get agreement eservices", () => { consumerId: tenant1.id, state: agreementState.pending, }; + const agreement4 = { + ...getMockAgreement(eservice4.id), + producerId: eservice4.producerId, + consumerId: tenant3.id, + state: agreementState.draft, + }; await addOneAgreement(agreement1); await addOneAgreement(agreement2); await addOneAgreement(agreement3); + await addOneAgreement(agreement4); }); it("should get all agreement eservices", async () => { @@ -99,9 +112,9 @@ describe("get agreement eservices", () => { ); expect(eservices).toEqual({ - totalCount: 3, + totalCount: 4, results: expect.arrayContaining( - [eservice1, eservice2, eservice3].map(toCompactEService) + [eservice1, eservice2, eservice3, eservice4].map(toCompactEService) ), }); }); @@ -120,9 +133,9 @@ describe("get agreement eservices", () => { ); expect(eservices).toEqual({ - totalCount: 2, + totalCount: 3, results: expect.arrayContaining( - [eservice1, eservice3].map(toCompactEService) + [eservice1, eservice3, eservice4].map(toCompactEService) ), }); }); @@ -141,9 +154,9 @@ describe("get agreement eservices", () => { ); expect(eservices).toEqual({ - totalCount: 2, + totalCount: 3, results: expect.arrayContaining( - [eservice1, eservice2].map(toCompactEService) + [eservice1, eservice2, eservice4].map(toCompactEService) ), }); }); @@ -223,7 +236,7 @@ describe("get agreement eservices", () => { ); expect(eservices).toEqual({ - totalCount: 1, + totalCount: 2, results: expect.arrayContaining([eservice3].map(toCompactEService)), }); }); @@ -261,7 +274,7 @@ describe("get agreement eservices", () => { ); expect(eservices).toEqual({ - totalCount: 3, + totalCount: 4, results: expect.arrayContaining( [eservice1, eservice2].map(toCompactEService) ), @@ -282,7 +295,7 @@ describe("get agreement eservices", () => { ); expect(eservices).toEqual({ - totalCount: 3, + totalCount: 4, results: expect.arrayContaining( [eservice2, eservice3].map(toCompactEService) ), @@ -307,4 +320,24 @@ describe("get agreement eservices", () => { results: [], }); }); + + it("should get agreement eservice for a delegated eservice with filters: producerId", async () => { + const agreements1 = await agreementService.getAgreementEServices( + { + producerIds: [eservice4.producerId], + eserviceName: undefined, + consumerIds: [], + agreeementStates: [], + }, + 10, + 0, + genericLogger + ); + expect(agreements1).toEqual({ + totalCount: 2, + results: expect.arrayContaining( + [eservice3, eservice4].map(toCompactEService) + ), + }); + }); }); diff --git a/packages/agreement-process/test/getAgreements.test.ts b/packages/agreement-process/test/getAgreements.test.ts index dd30dac922..ad65d13b21 100644 --- a/packages/agreement-process/test/getAgreements.test.ts +++ b/packages/agreement-process/test/getAgreements.test.ts @@ -4,6 +4,7 @@ import { getMockDescriptorPublished, getMockEService, getMockAgreement, + getMockDelegation, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -17,6 +18,7 @@ import { EServiceId, agreementState, TenantId, + delegationKind, } from "pagopa-interop-models"; import { describe, beforeEach, it, expect } from "vitest"; import { @@ -24,6 +26,7 @@ import { addOneEService, addOneAgreement, agreementService, + addOneDelegation, } from "./utils.js"; describe("get agreements", () => { @@ -38,6 +41,7 @@ describe("get agreements", () => { let eservice1: EService; let eservice2: EService; let eservice3: EService; + let eservice4: EService; let attribute1: AgreementAttribute; let attribute2: AgreementAttribute; let attribute3: AgreementAttribute; @@ -48,6 +52,7 @@ describe("get agreements", () => { let agreement4: Agreement; let agreement5: Agreement; let agreement6: Agreement; + let agreement7: Agreement; beforeEach(async () => { tenant1 = getMockTenant(); @@ -92,6 +97,10 @@ describe("get agreements", () => { ...getMockEService(generateId(), tenant3.id, [descriptor5]), name: "EService3", // Adding name because results are sorted by esevice name }; + eservice4 = { + ...getMockEService(generateId(), tenant3.id, [descriptor5]), + name: "EService4", // Adding name because results are sorted by esevice name + }; await addOneTenant(tenant1); await addOneTenant(tenant2); @@ -99,6 +108,7 @@ describe("get agreements", () => { await addOneEService(eservice1); await addOneEService(eservice2); await addOneEService(eservice3); + await addOneEService(eservice4); attribute1 = { id: generateId() }; attribute2 = { id: generateId() }; @@ -149,12 +159,27 @@ describe("get agreements", () => { producerId: eservice3.producerId, }; + agreement7 = { + ...getMockAgreement(eservice4.id, tenant1.id, agreementState.draft), + descriptorId: eservice4.descriptors[0].id, + producerId: eservice4.producerId, + }; + await addOneAgreement(agreement1); await addOneAgreement(agreement2); await addOneAgreement(agreement3); await addOneAgreement(agreement4); await addOneAgreement(agreement5); await addOneAgreement(agreement6); + await addOneAgreement(agreement7); + + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: eservice4.producerId, + eserviceId: eservice4.id, + state: agreementState.active, + }); + await addOneDelegation(delegation1); }); it("should get all agreements if no filters are provided", async () => { @@ -165,7 +190,7 @@ describe("get agreements", () => { genericLogger ); expect(allAgreements).toEqual({ - totalCount: 6, + totalCount: 7, results: expect.arrayContaining([ agreement1, agreement2, @@ -173,6 +198,7 @@ describe("get agreements", () => { agreement4, agreement5, agreement6, + agreement7, ]), }); }); @@ -221,8 +247,13 @@ describe("get agreements", () => { genericLogger ); expect(agreements1).toEqual({ - totalCount: 3, - results: expect.arrayContaining([agreement1, agreement3, agreement5]), + totalCount: 4, + results: expect.arrayContaining([ + agreement1, + agreement3, + agreement5, + agreement7, + ]), }); const agreements2 = await agreementService.getAgreements( @@ -234,13 +265,14 @@ describe("get agreements", () => { genericLogger ); expect(agreements2).toEqual({ - totalCount: 5, + totalCount: 6, results: expect.arrayContaining([ agreement1, agreement2, agreement3, agreement4, agreement5, + agreement7, ]), }); }); @@ -301,12 +333,13 @@ describe("get agreements", () => { genericLogger ); expect(agreements2).toEqual({ - totalCount: 4, + totalCount: 5, results: expect.arrayContaining([ agreement1, agreement3, agreement5, agreement6, + agreement7, ]), }); }); @@ -496,6 +529,7 @@ describe("get agreements", () => { results: expect.arrayContaining([agreement2, agreement3]), }); }); + it("should get no agreements in case no filters match", async () => { const agreements = await agreementService.getAgreements( { @@ -511,4 +545,19 @@ describe("get agreements", () => { results: [], }); }); + + it("should get agreements for a delegated eservice with filters: producerId", async () => { + const agreements = await agreementService.getAgreements( + { + producerId: eservice4.producerId, + }, + 10, + 0, + genericLogger + ); + expect(agreements).toEqual({ + totalCount: 3, + results: expect.arrayContaining([agreement5, agreement6, agreement7]), + }); + }); }); diff --git a/packages/agreement-process/test/rejectAgreement.test.ts b/packages/agreement-process/test/rejectAgreement.test.ts index bdbf0d2af2..05c98d47d8 100644 --- a/packages/agreement-process/test/rejectAgreement.test.ts +++ b/packages/agreement-process/test/rejectAgreement.test.ts @@ -6,6 +6,7 @@ import { getMockAgreement, getMockCertifiedTenantAttribute, getMockDeclaredTenantAttribute, + getMockDelegation, getMockDescriptorPublished, getMockEService, getMockEServiceAttribute, @@ -26,6 +27,8 @@ import { TenantId, VerifiedTenantAttribute, agreementState, + delegationKind, + delegationState, generateId, toAgreementV2, } from "pagopa-interop-models"; @@ -42,6 +45,7 @@ import { } from "../src/model/domain/errors.js"; import { addOneAgreement, + addOneDelegation, addOneEService, addOneTenant, agreementService, @@ -49,181 +53,212 @@ import { } from "./utils.js"; describe("reject agreement", () => { - it("should succeed when requester is Producer and the Agreement is in a rejectable state", async () => { - vi.useFakeTimers(); - vi.setSystemTime(new Date()); - - const producerId = generateId(); - const tenantCertifiedAttribute: CertifiedTenantAttribute = { - ...getMockCertifiedTenantAttribute(), - revocationTimestamp: undefined, - }; - const revokedTenantCertifiedAttribute: CertifiedTenantAttribute = { - ...getMockCertifiedTenantAttribute(), - revocationTimestamp: new Date(), - }; - - const tenantDeclaredAttribute: DeclaredTenantAttribute = { - ...getMockDeclaredTenantAttribute(), - revocationTimestamp: undefined, - }; - const revokedTenantDeclaredAttribute: DeclaredTenantAttribute = { - ...getMockDeclaredTenantAttribute(), - revocationTimestamp: new Date(), - }; + it.each([ + { + desc: "Producer", + type: "producer", + }, + { + desc: "Delegate of an active delegation", + type: "delegate", + }, + ])( + "should succeed when requester is $desc and the Agreement is in a rejectable state", + async ({ type }) => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); - const tenantVerifiedAttribute: VerifiedTenantAttribute = { - ...getMockVerifiedTenantAttribute(), - verifiedBy: [ - { - id: producerId, - verificationDate: new Date(), - extensionDate: addDays(new Date(), 30), - }, - ], - }; + const producerId = generateId(); + const tenantCertifiedAttribute: CertifiedTenantAttribute = { + ...getMockCertifiedTenantAttribute(), + revocationTimestamp: undefined, + }; + const revokedTenantCertifiedAttribute: CertifiedTenantAttribute = { + ...getMockCertifiedTenantAttribute(), + revocationTimestamp: new Date(), + }; - const tenantVerifiedAttributeByAnotherProducer: VerifiedTenantAttribute = { - ...getMockVerifiedTenantAttribute(), - verifiedBy: [ - { id: generateId(), verificationDate: new Date() }, - ], - }; + const tenantDeclaredAttribute: DeclaredTenantAttribute = { + ...getMockDeclaredTenantAttribute(), + revocationTimestamp: undefined, + }; + const revokedTenantDeclaredAttribute: DeclaredTenantAttribute = { + ...getMockDeclaredTenantAttribute(), + revocationTimestamp: new Date(), + }; - const tenantVerfiedAttributeWithExpiredExtension: VerifiedTenantAttribute = - { + const tenantVerifiedAttribute: VerifiedTenantAttribute = { ...getMockVerifiedTenantAttribute(), verifiedBy: [ { id: producerId, verificationDate: new Date(), - extensionDate: new Date(), + extensionDate: addDays(new Date(), 30), }, ], }; - const consumer: Tenant = { - ...getMockTenant(), - attributes: [ - tenantCertifiedAttribute, - revokedTenantCertifiedAttribute, - tenantDeclaredAttribute, - revokedTenantDeclaredAttribute, - tenantVerifiedAttribute, - tenantVerifiedAttributeByAnotherProducer, - tenantVerfiedAttributeWithExpiredExtension, - // Adding some attributes not matching with descriptor attributes - // to test that they are not kept in the agreement - getMockVerifiedTenantAttribute(), - getMockCertifiedTenantAttribute(), - getMockDeclaredTenantAttribute(), - ], - }; - const descriptor: Descriptor = { - ...getMockDescriptorPublished(), - attributes: { - // I add also some attributes not matching with tenant attributes - // to test that they are not kept in the agreement - certified: [ - [ - getMockEServiceAttribute(tenantCertifiedAttribute.id), - getMockEServiceAttribute(revokedTenantCertifiedAttribute.id), - getMockEServiceAttribute(), + const tenantVerifiedAttributeByAnotherProducer: VerifiedTenantAttribute = + { + ...getMockVerifiedTenantAttribute(), + verifiedBy: [ + { id: generateId(), verificationDate: new Date() }, ], - ], - verified: [ - [ - getMockEServiceAttribute(tenantVerifiedAttribute.id), - getMockEServiceAttribute( - tenantVerifiedAttributeByAnotherProducer.id - ), - getMockEServiceAttribute( - tenantVerfiedAttributeWithExpiredExtension.id - ), - getMockEServiceAttribute(), + }; + + const tenantVerfiedAttributeWithExpiredExtension: VerifiedTenantAttribute = + { + ...getMockVerifiedTenantAttribute(), + verifiedBy: [ + { + id: producerId, + verificationDate: new Date(), + extensionDate: addDays(new Date(), 300), + }, ], + }; + + const consumer: Tenant = { + ...getMockTenant(), + attributes: [ + tenantCertifiedAttribute, + revokedTenantCertifiedAttribute, + tenantDeclaredAttribute, + revokedTenantDeclaredAttribute, + tenantVerifiedAttribute, + tenantVerifiedAttributeByAnotherProducer, + tenantVerfiedAttributeWithExpiredExtension, + // Adding some attributes not matching with descriptor attributes + // to test that they are not kept in the agreement + getMockVerifiedTenantAttribute(), + getMockCertifiedTenantAttribute(), + getMockDeclaredTenantAttribute(), ], - declared: [ - [ - getMockEServiceAttribute(tenantDeclaredAttribute.id), - getMockEServiceAttribute(revokedTenantDeclaredAttribute.id), - getMockEServiceAttribute(), + }; + const descriptor: Descriptor = { + ...getMockDescriptorPublished(), + attributes: { + // I add also some attributes not matching with tenant attributes + // to test that they are not kept in the agreement + certified: [ + [ + getMockEServiceAttribute(tenantCertifiedAttribute.id), + getMockEServiceAttribute(revokedTenantCertifiedAttribute.id), + getMockEServiceAttribute(), + ], ], - ], - }, - }; - const eservice: EService = { - ...getMockEService(), - producerId, - descriptors: [descriptor], - }; + verified: [ + [ + getMockEServiceAttribute(tenantVerifiedAttribute.id), + getMockEServiceAttribute( + tenantVerifiedAttributeByAnotherProducer.id + ), + getMockEServiceAttribute( + tenantVerfiedAttributeWithExpiredExtension.id + ), + getMockEServiceAttribute(), + ], + ], + declared: [ + [ + getMockEServiceAttribute(tenantDeclaredAttribute.id), + getMockEServiceAttribute(revokedTenantDeclaredAttribute.id), + getMockEServiceAttribute(), + ], + ], + }, + }; + const eservice: EService = { + ...getMockEService(), + producerId, + descriptors: [descriptor], + }; - const agreement = { - ...getMockAgreement(), - eserviceId: eservice.id, - producerId: eservice.producerId, - descriptorId: descriptor.id, - consumerId: consumer.id, - state: randomArrayItem(agreementRejectableStates), - }; - await addOneTenant(consumer); - await addOneEService(eservice); - await addOneAgreement(agreement); + const agreement = { + ...getMockAgreement(), + eserviceId: eservice.id, + producerId: eservice.producerId, + descriptorId: descriptor.id, + consumerId: consumer.id, + state: randomArrayItem(agreementRejectableStates), + }; + await addOneTenant(consumer); + await addOneEService(eservice); + await addOneAgreement(agreement); - const authData = getRandomAuthData(agreement.producerId); - const returnedAgreement = await agreementService.rejectAgreement( - agreement.id, - "Rejected by producer due to test reasons", - { - authData, - serviceName: "", - correlationId: generateId(), - logger: genericLogger, + const authData = + type === "producer" + ? getRandomAuthData(agreement.producerId) + : getRandomAuthData(); + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: authData.organizationId, + eserviceId: eservice.id, + state: delegationState.active, + }); + if (type === "delegate") { + await addOneDelegation(delegation); } - ); - const agreementEvent = await readLastAgreementEvent(agreement.id); + const returnedAgreement = await agreementService.rejectAgreement( + agreement.id, + "Rejected by producer due to test reasons", + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); - expect(agreementEvent).toMatchObject({ - type: "AgreementRejected", - event_version: 2, - version: "1", - stream_id: agreement.id, - }); + const agreementEvent = await readLastAgreementEvent(agreement.id); + + expect(agreementEvent).toMatchObject({ + type: "AgreementRejected", + event_version: 2, + version: "1", + stream_id: agreement.id, + }); - const actualAgreementRejected = decodeProtobufPayload({ - messageType: AgreementRejectedV2, - payload: agreementEvent.data, - }).agreement; + const actualAgreementRejected = decodeProtobufPayload({ + messageType: AgreementRejectedV2, + payload: agreementEvent.data, + }).agreement; - /* We must delete some properties because the rejection + /* We must delete some properties because the rejection sets them to undefined thus and the protobuf serialization strips them from the payload */ - delete agreement.suspendedByConsumer; - delete agreement.suspendedByProducer; - delete agreement.suspendedByPlatform; - const expectedAgreemenentRejected: Agreement = { - ...agreement, - state: agreementState.rejected, - rejectionReason: "Rejected by producer due to test reasons", - // Keeps only not revoked attributes that are matching in descriptor and tenant - verifiedAttributes: [{ id: tenantVerifiedAttribute.id }], - declaredAttributes: [{ id: tenantDeclaredAttribute.id }], - certifiedAttributes: [{ id: tenantCertifiedAttribute.id }], - stamps: { - ...agreement.stamps, - rejection: { - who: authData.userId, - when: new Date(), + delete agreement.suspendedByConsumer; + delete agreement.suspendedByProducer; + delete agreement.suspendedByPlatform; + const expectedAgreementRejected: Agreement = { + ...agreement, + state: agreementState.rejected, + rejectionReason: "Rejected by producer due to test reasons", + // Keeps only not revoked attributes that are matching in descriptor and tenant + verifiedAttributes: [ + { id: tenantVerifiedAttribute.id }, + { id: tenantVerfiedAttributeWithExpiredExtension.id }, + ], + declaredAttributes: [{ id: tenantDeclaredAttribute.id }], + certifiedAttributes: [{ id: tenantCertifiedAttribute.id }], + stamps: { + ...agreement.stamps, + rejection: { + who: authData.userId, + when: new Date(), + ...(type === "delegate" ? { delegationId: delegation.id } : {}), + }, }, - }, - }; - expect(actualAgreementRejected).toMatchObject( - toAgreementV2(expectedAgreemenentRejected) - ); - expect(actualAgreementRejected).toEqual(toAgreementV2(returnedAgreement)); - vi.useRealTimers(); - }); + }; + expect(actualAgreementRejected).toMatchObject( + toAgreementV2(expectedAgreementRejected) + ); + expect(actualAgreementRejected).toEqual(toAgreementV2(returnedAgreement)); + vi.useRealTimers(); + } + ); it("should throw an agreementNotFound error when the agreement does not exist", async () => { await addOneAgreement(getMockAgreement()); @@ -379,4 +414,89 @@ describe("reject agreement", () => { descriptorNotFound(eservice.id, agreement.descriptorId) ); }); + + it("should throw operationNotAllowed when the requester is the producer and there is an active delegation", async () => { + const eservice: EService = { + ...getMockEService(), + descriptors: [getMockDescriptorPublished()], + }; + const consumer = getMockTenant(); + const delegate = getMockTenant(); + const agreement = { + ...getMockAgreement(), + state: randomArrayItem(agreementRejectableStates), + eserviceId: eservice.id, + producerId: eservice.producerId, + consumerId: consumer.id, + descriptorId: eservice.descriptors[0].id, + }; + const authData = getRandomAuthData(agreement.producerId); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: delegate.id, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneAgreement(agreement); + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneTenant(delegate); + await addOneDelegation(delegation); + + await expect( + agreementService.rejectAgreement( + agreement.id, + "Rejected by producer due to test reasons", + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); + }); + + it("should throw a operationNotAllowed error when the requester is the delegate but the delegation in not active", async () => { + const eservice: EService = { + ...getMockEService(), + descriptors: [getMockDescriptorPublished()], + }; + const consumer = getMockTenant(); + const agreement = { + ...getMockAgreement(), + state: randomArrayItem(agreementRejectableStates), + eserviceId: eservice.id, + producerId: eservice.producerId, + consumerId: consumer.id, + descriptorId: eservice.descriptors[0].id, + }; + const authData = getRandomAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: authData.organizationId, + eserviceId: eservice.id, + state: delegationState.waitingForApproval, + }); + + await addOneAgreement(agreement); + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneDelegation(delegation); + + await expect( + agreementService.rejectAgreement( + agreement.id, + "Rejected by producer due to test reasons", + + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); + }); }); diff --git a/packages/agreement-process/test/submitAgreement.test.ts b/packages/agreement-process/test/submitAgreement.test.ts index 738f02a4f3..e90910119f 100644 --- a/packages/agreement-process/test/submitAgreement.test.ts +++ b/packages/agreement-process/test/submitAgreement.test.ts @@ -15,6 +15,7 @@ import { getMockAgreement, getMockCertifiedTenantAttribute, getMockDeclaredTenantAttribute, + getMockDelegation, getMockDescriptor, getMockEService, getMockEServiceAttribute, @@ -27,6 +28,7 @@ import { import { Agreement, AgreementActivatedV2, + AgreementContractPDFPayload, AgreementId, AgreementSetMissingCertifiedAttributesByPlatformV2, AgreementSubmittedV2, @@ -35,12 +37,16 @@ import { DescriptorId, DescriptorState, EServiceId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, SelfcareId, + Tenant, TenantAttribute, TenantId, agreementApprovalPolicy, agreementState, attributeKind, + delegationKind, + delegationState, descriptorState, fromAgreementV2, generateId, @@ -66,6 +72,7 @@ import { config } from "../src/config/config.js"; import { addOneAgreement, addOneAttribute, + addOneDelegation, addOneEService, addOneTenant, agreementService, @@ -445,6 +452,55 @@ describe("submit agreement", () => { ).rejects.toThrowError(notLatestEServiceDescriptor(agreement.descriptorId)); }); + it("should throw a notLatestEServiceDescriptor error when eservice has only a waiting for approval descriptor (no active descriptors)", async () => { + const producer = getMockTenant(); + const consumer = { + ...getMockTenant(), + mails: [ + { + id: generateId(), + kind: tenantMailKind.ContactEmail, + address: "avalidemailaddressfortenant@testingagreement.com", + createdAt: new Date(), + }, + ], + }; + + const descriptor = { + ...getMockDescriptor(), + state: descriptorState.waitingForApproval, + }; + const eservice = getMockEService(generateId(), producer.id, [ + descriptor, + ]); + + const agreement = { + ...getMockAgreement(eservice.id, consumer.id), + producerId: producer.id, + descriptorId: eservice.descriptors[0].id, + }; + + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneTenant(producer); + await addOneAgreement(agreement); + + const authData = getRandomAuthData(consumer.id); + + await expect( + agreementService.submitAgreement( + agreement.id, + { consumerNotes: "This is a test" }, + { + authData, + correlationId: generateId(), + serviceName: "AgreementServiceTest", + logger: genericLogger, + } + ) + ).rejects.toThrowError(notLatestEServiceDescriptor(agreement.descriptorId)); + }); + it("should throw a notLatestEServiceDescriptor error when the agreement descriptor does not exist in the eservice descriptors", async () => { const producer = getMockTenant(); const consumer = { @@ -514,7 +570,9 @@ describe("submit agreement", () => { id: descriptorId, state: randomArrayItem( Object.values(descriptorState).filter( - (state: DescriptorState) => state !== descriptorState.draft + (state: DescriptorState) => + state !== descriptorState.draft && + state !== descriptorState.waitingForApproval ) ), version: "1", @@ -524,7 +582,9 @@ describe("submit agreement", () => { ...getMockDescriptor(), state: randomArrayItem( Object.values(descriptorState).filter( - (state: DescriptorState) => state !== descriptorState.draft + (state: DescriptorState) => + state !== descriptorState.draft && + state !== descriptorState.waitingForApproval ) ), version: "2", @@ -584,7 +644,9 @@ describe("submit agreement", () => { state: randomArrayItem( Object.values(descriptorState).filter( (state: DescriptorState) => - !allowedStatus.includes(state) && state !== descriptorState.draft + !allowedStatus.includes(state) && + state !== descriptorState.draft && + state !== descriptorState.waitingForApproval ) ), }; @@ -1500,6 +1562,308 @@ describe("submit agreement", () => { }); }); + it("should create a new agreement contract for first activation with new state ACTIVE when producer and consumer are different, generates AgreementActivated and eservice has a delegation", async () => { + vi.spyOn(pdfGenerator, "generate"); + const consumerId = generateId(); + const producer = getMockTenant(); + const consumerNotesText = "This is a test"; + + const validVerifiedTenantAttribute: TenantAttribute = { + ...getMockVerifiedTenantAttribute(), + verifiedBy: [ + { + id: producer.id, + verificationDate: new Date(new Date().getFullYear() - 1), + expirationDate: undefined, + extensionDate: undefined, + }, + ], + }; + + const validCertifiedTenantAttribute: TenantAttribute = { + ...getMockCertifiedTenantAttribute(), + revocationTimestamp: undefined, + }; + + const validDeclaredTenantAttribute: TenantAttribute = { + ...getMockDeclaredTenantAttribute(), + revocationTimestamp: undefined, + }; + + const consumer = { + ...getMockTenant(consumerId, [ + validVerifiedTenantAttribute, + validCertifiedTenantAttribute, + validDeclaredTenantAttribute, + ]), + selfcareId: generateId(), + mails: [ + { + id: generateId(), + kind: tenantMailKind.ContactEmail, + address: "avalidemailaddressfortenant@testingagreement.com", + createdAt: new Date(), + }, + ], + }; + + const descriptor = { + ...getMockDescriptor(), + state: descriptorState.suspended, + agreementApprovalPolicy: agreementApprovalPolicy.automatic, + attributes: { + certified: [ + [getMockEServiceAttribute(validCertifiedTenantAttribute.id)], + ], + declared: [[getMockEServiceAttribute(validDeclaredTenantAttribute.id)]], + verified: [[getMockEServiceAttribute(validVerifiedTenantAttribute.id)]], + }, + }; + + const eservice = getMockEService(generateId(), producer.id, [ + descriptor, + ]); + + const authData = getRandomAuthData(consumer.id); + const agreement: Agreement = { + ...getMockAgreement(eservice.id, consumer.id), + producerId: producer.id, + descriptorId: eservice.descriptors[0].id, + state: agreementState.draft, + suspendedByConsumer: randomBoolean(), + suspendedByProducer: randomBoolean(), + stamps: { + suspensionByConsumer: createStamp(authData.userId), + suspensionByProducer: createStamp(authData.userId), + }, + suspendedAt: new Date(), + // The agreement is draft, so it doens't have a contract or attributes + contract: undefined, + certifiedAttributes: [], + declaredAttributes: [], + verifiedAttributes: [], + }; + + const verifiedAttribute: Attribute = { + id: validVerifiedTenantAttribute.id, + kind: attributeKind.verified, + description: "A verified attribute", + name: "A verified attribute name", + creationTime: new Date(new Date().getFullYear() - 1), + }; + + const declareAttribute: Attribute = { + id: validDeclaredTenantAttribute.id, + kind: attributeKind.declared, + description: "A declared attribute", + name: "A declared attribute name", + creationTime: new Date(new Date().getFullYear() - 1), + }; + + const certifiedAttribute: Attribute = { + id: validCertifiedTenantAttribute.id, + kind: attributeKind.certified, + description: "A certified attribute", + name: "A certified attribute name", + creationTime: new Date(new Date().getFullYear() - 1), + }; + + const delegate = getMockTenant(generateId()); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + state: delegationState.active, + delegatorId: producer.id, + delegateId: delegate.id, + eserviceId: eservice.id, + }); + + await addOneTenant(delegate); + await addOneDelegation(delegation); + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneTenant(producer); + await addOneAttribute(verifiedAttribute); + await addOneAttribute(declareAttribute); + await addOneAttribute(certifiedAttribute); + await addOneAgreement(agreement); + + const submittedAgreement = await agreementService.submitAgreement( + agreement.id, + { + consumerNotes: consumerNotesText, + }, + { + authData, + correlationId: generateId(), + serviceName: "AgreementServiceTest", + logger: genericLogger, + } + ); + + expect(submittedAgreement).toBeDefined(); + expect(submittedAgreement.state).toBe(agreementState.active); + + const uploadedFiles = await fileManager.listFiles( + config.s3Bucket, + genericLogger + ); + + expect(submittedAgreement.contract).toBeDefined(); + expect(uploadedFiles[0]).toEqual(submittedAgreement.contract?.path); + + const actualAgreementData = await readLastAgreementEvent(agreement.id); + if (!actualAgreementData) { + fail("Creation fails: agreement not found in event-store"); + } + + expect(actualAgreementData.type).toEqual("AgreementActivated"); + expect(actualAgreementData).toMatchObject({ + type: "AgreementActivated", + event_version: 2, + version: "1", + stream_id: submittedAgreement.id, + }); + + const actualAgreement: AgreementV2 | undefined = decodeProtobufPayload({ + messageType: AgreementActivatedV2, + payload: actualAgreementData.data, + }).agreement; + + if (!actualAgreement) { + fail("impossible to decode AgreementAddedV1 data"); + } + + const contractDocumentId = submittedAgreement.contract!.id; + const contractCreatedAt = submittedAgreement.contract!.createdAt; + const contractDocumentName = `${consumer.id}_${ + producer.id + }_${formatDateyyyyMMddHHmmss(contractCreatedAt)}_agreement_contract.pdf`; + + const expectedContract = { + id: contractDocumentId, + contentType: "application/pdf", + createdAt: contractCreatedAt, + path: `${config.agreementContractsPath}/${agreement.id}/${contractDocumentId}/${contractDocumentName}`, + prettyName: "Richiesta di fruizione", + name: contractDocumentName, + }; + + const expectedSubmitterId = authData.userId; + const expectedActivatorId = authData.userId; + const expectedAgreement = { + ...agreement, + state: agreementState.active, + consumerNotes: consumerNotesText, + contract: expectedContract, + suspendedByPlatform: false, + certifiedAttributes: [ + { + id: validCertifiedTenantAttribute.id, + }, + ], + declaredAttributes: [ + { + id: validDeclaredTenantAttribute.id, + }, + ], + verifiedAttributes: [ + { + id: validVerifiedTenantAttribute.id, + }, + ], + stamps: { + ...agreement.stamps, + submission: { + who: expectedSubmitterId, + when: submittedAgreement.stamps?.submission?.when, + }, + activation: { + who: expectedActivatorId, + when: submittedAgreement.stamps?.activation?.when, + }, + }, + }; + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(expectedContract.path); + + expect(fromAgreementV2(actualAgreement)).toEqual(expectedAgreement); + expect(submittedAgreement).toEqual(expectedAgreement); + + const getIpaCode = (tenant: Tenant): string | undefined => + tenant.externalId.origin === PUBLIC_ADMINISTRATIONS_IDENTIFIER + ? tenant.externalId.value + : undefined; + + const expectedAgreementPDFPayload: AgreementContractPDFPayload = { + todayDate: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + todayTime: expect.stringMatching(/^\d{2}:\d{2}:\d{2}$/), + agreementId: agreement.id, + submitterId: expectedSubmitterId, + submissionDate: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + submissionTime: expect.stringMatching(/^\d{2}:\d{2}:\d{2}$/), + activatorId: expectedActivatorId, + activationDate: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + activationTime: expect.stringMatching(/^\d{2}:\d{2}:\d{2}$/), + eserviceName: eservice.name, + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + descriptorVersion: eservice.descriptors[0].version, + producerName: producer.name, + producerIpaCode: getIpaCode(producer), + consumerName: consumer.name, + consumerIpaCode: getIpaCode(consumer), + certifiedAttributes: [ + { + assignmentDate: dateAtRomeZone( + validCertifiedTenantAttribute.assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + validCertifiedTenantAttribute.assignmentTimestamp + ), + attributeName: certifiedAttribute.name, + attributeId: validCertifiedTenantAttribute.id, + }, + ], + declaredAttributes: [ + { + assignmentDate: dateAtRomeZone( + validDeclaredTenantAttribute.assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + validDeclaredTenantAttribute.assignmentTimestamp + ), + attributeName: declareAttribute.name, + attributeId: validDeclaredTenantAttribute.id, + }, + ], + verifiedAttributes: [ + { + assignmentDate: dateAtRomeZone( + validVerifiedTenantAttribute.assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + validVerifiedTenantAttribute.assignmentTimestamp + ), + attributeName: verifiedAttribute.name, + attributeId: validVerifiedTenantAttribute.id, + expirationDate: undefined, + }, + ], + producerDelegationId: delegation.id, + producerDelegatorName: producer.name, + producerDelegatorIpaCode: getIpaCode(producer), + producerDelegateName: delegate.name, + producerDelegateIpaCode: getIpaCode(delegate), + }; + + expect(pdfGenerator.generate).toHaveBeenCalledWith( + expect.any(String), + expectedAgreementPDFPayload + ); + }); + it("should create a new agreement contract for first activation with new state ACTIVE when producer and consumer are different, generates AgreementActivated", async () => { const consumerId = generateId(); const producer = getMockTenant(consumerId); diff --git a/packages/agreement-process/test/suspendAgreement.test.ts b/packages/agreement-process/test/suspendAgreement.test.ts index b946783481..d815995581 100644 --- a/packages/agreement-process/test/suspendAgreement.test.ts +++ b/packages/agreement-process/test/suspendAgreement.test.ts @@ -10,6 +10,7 @@ import { getMockAgreementAttribute, getMockCertifiedTenantAttribute, getMockDeclaredTenantAttribute, + getMockDelegation, getMockDescriptorPublished, getMockEService, getMockEServiceAttribute, @@ -30,6 +31,8 @@ import { Tenant, TenantId, agreementState, + delegationKind, + delegationState, generateId, toAgreementV2, } from "pagopa-interop-models"; @@ -46,6 +49,7 @@ import { import { createStamp } from "../src/services/agreementStampUtils.js"; import { addOneAgreement, + addOneDelegation, addOneEService, addOneTenant, agreementService, @@ -181,12 +185,10 @@ describe("suspend agreement", () => { ...expectedStamps, }, }; - expect(actualAgreementSuspended).toMatchObject( + expect(actualAgreementSuspended).toEqual( toAgreementV2(expectedAgreementSuspended) ); - expect(actualAgreementSuspended).toMatchObject( - toAgreementV2(returnedAgreement) - ); + expect(actualAgreementSuspended).toEqual(toAgreementV2(returnedAgreement)); }); it("should succeed when requester is Consumer or Producer, Agreement producer and consumer are the same, and the Agreement is in an suspendable state", async () => { @@ -285,12 +287,10 @@ describe("suspend agreement", () => { }, }, }; - expect(actualAgreementSuspended).toMatchObject( + expect(actualAgreementSuspended).toEqual( toAgreementV2(expectedAgreementSuspended) ); - expect(actualAgreementSuspended).toMatchObject( - toAgreementV2(returnedAgreement) - ); + expect(actualAgreementSuspended).toEqual(toAgreementV2(returnedAgreement)); }); it("should preserve the suspension flags and the stamps that it does not update", async () => { @@ -393,14 +393,87 @@ describe("suspend agreement", () => { ...expectedStamps, }, }; - expect(actualAgreementSuspended).toMatchObject( + expect(actualAgreementSuspended).toEqual( toAgreementV2(expectedAgreementSuspended) ); - expect(actualAgreementSuspended).toMatchObject( - toAgreementV2(returnedAgreement) - ); + expect(actualAgreementSuspended).toEqual(toAgreementV2(returnedAgreement)); }); + it.each(agreementSuspendableStates)( + "should succeed if the requester is the delegate and the agreement is in state %s", + async (state) => { + const consumer: Tenant = { + ...getMockTenant(), + attributes: [ + getMockCertifiedTenantAttribute(), + getMockDeclaredTenantAttribute(), + getMockVerifiedTenantAttribute(), + ], + }; + + const descriptor = { + ...getMockDescriptorPublished(), + attributes: { + certified: [[getMockEServiceAttribute(consumer.attributes[0].id)]], + declared: [[getMockEServiceAttribute(consumer.attributes[1].id)]], + verified: [[getMockEServiceAttribute(consumer.attributes[2].id)]], + }, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const agreement = { + ...getMockAgreement(), + state, + eserviceId: eservice.id, + producerId: eservice.producerId, + consumerId: consumer.id, + descriptorId: descriptor.id, + suspendedByConsumer: false, + suspendedByProducer: false, + suspendedByPlatform: false, + }; + const authData = getRandomAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: authData.organizationId, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneAgreement(agreement); + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneDelegation(delegation); + + const expectedAgreement = { + ...agreement, + state: agreementState.suspended, + suspendedByProducer: true, + stamps: { + ...agreement.stamps, + suspensionByProducer: { + delegationId: delegation.id, + who: authData.userId, + when: new Date(), + }, + }, + }; + + const actualAgreement = await agreementService.suspendAgreement( + agreement.id, + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); + expect(actualAgreement).toEqual(expectedAgreement); + } + ); + it("should throw an agreementNotFound error when the agreement does not exist", async () => { await addOneAgreement(getMockAgreement()); const authData = getRandomAuthData(); @@ -530,4 +603,80 @@ describe("suspend agreement", () => { descriptorNotFound(eservice.id, agreement.descriptorId) ); }); + + it("should throw a operationNotAllowed error when the requester is the producer but not the delegate", async () => { + const eservice: EService = { + ...getMockEService(), + descriptors: [getMockDescriptorPublished()], + }; + const consumer = getMockTenant(); + const delegate = getMockTenant(); + const agreement = { + ...getMockAgreement(), + state: randomArrayItem(agreementSuspendableStates), + eserviceId: eservice.id, + producerId: eservice.producerId, + consumerId: consumer.id, + descriptorId: eservice.descriptors[0].id, + }; + const authData = getRandomAuthData(agreement.producerId); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: delegate.id, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneAgreement(agreement); + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneTenant(delegate); + await addOneDelegation(delegation); + + await expect( + agreementService.suspendAgreement(agreement.id, { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); + }); + + it("should throw a operationNotAllowed error when the requester is the delegate but the delegation in not active", async () => { + const eservice: EService = { + ...getMockEService(), + descriptors: [getMockDescriptorPublished()], + }; + const consumer = getMockTenant(); + const agreement = { + ...getMockAgreement(), + state: randomArrayItem(agreementSuspendableStates), + eserviceId: eservice.id, + producerId: eservice.producerId, + consumerId: consumer.id, + descriptorId: eservice.descriptors[0].id, + }; + const authData = getRandomAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: authData.organizationId, + eserviceId: eservice.id, + state: delegationState.waitingForApproval, + }); + + await addOneAgreement(agreement); + await addOneEService(eservice); + await addOneTenant(consumer); + await addOneDelegation(delegation); + + await expect( + agreementService.suspendAgreement(agreement.id, { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError(operationNotAllowed(authData.organizationId)); + }); }); diff --git a/packages/agreement-process/test/upgradeAgreement.test.ts b/packages/agreement-process/test/upgradeAgreement.test.ts index 5013355ef2..d28b90217d 100644 --- a/packages/agreement-process/test/upgradeAgreement.test.ts +++ b/packages/agreement-process/test/upgradeAgreement.test.ts @@ -3,9 +3,11 @@ /* eslint-disable sonarjs/cognitive-complexity */ /* eslint-disable fp/no-delete */ import { + dateAtRomeZone, FileManagerError, formatDateyyyyMMddHHmmss, genericLogger, + timeAtRomeZone, } from "pagopa-interop-commons"; import { decodeProtobufPayload, @@ -13,6 +15,7 @@ import { getMockAttribute, getMockCertifiedTenantAttribute, getMockDeclaredTenantAttribute, + getMockDelegation, getMockDescriptorPublished, getMockEService, getMockEServiceAttribute, @@ -26,16 +29,20 @@ import { Agreement, AgreementAddedV2, AgreementArchivedByUpgradeV2, + AgreementContractPDFPayload, AgreementDocument, AgreementId, AgreementUpgradedV2, Descriptor, EService, EServiceId, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, Tenant, TenantId, agreementState, attributeKind, + delegationKind, + delegationState, descriptorState, fromAgreementV2, generateId, @@ -58,25 +65,28 @@ import { tenantNotFound, unexpectedVersionFormat, } from "../src/model/domain/errors.js"; -import { createStamp } from "../src/services/agreementStampUtils.js"; import { config } from "../src/config/config.js"; +import { createStamp } from "../src/services/agreementStampUtils.js"; import { addOneAgreement, addOneAttribute, + addOneDelegation, addOneEService, addOneTenant, agreementService, fileManager, getMockConsumerDocument, getMockContract, + pdfGenerator, readAgreementEventByVersion, uploadDocument, } from "./utils.js"; describe("upgrade Agreement", () => { + const currentExecutionTime = new Date(); beforeAll(() => { vi.useFakeTimers(); - vi.setSystemTime(new Date()); + vi.setSystemTime(currentExecutionTime); }); afterAll(() => { vi.useRealTimers(); @@ -544,6 +554,335 @@ describe("upgrade Agreement", () => { } }); + it("should succeed with valid Verified, Certified, and Declared attributes when consumer and producer are different and requester is a delegate", async () => { + const producer = getMockTenant(); + + const validVerifiedTenantAttribute = { + ...getMockVerifiedTenantAttribute(), + verifiedBy: [ + { + id: producer.id, + verificationDate: new Date(), + expirationDate: undefined, + extensionDate: undefined, + }, + ], + }; + const verifiedAttribute = getMockAttribute( + attributeKind.verified, + validVerifiedTenantAttribute.id + ); + await addOneAttribute(verifiedAttribute); + + const validDeclaredTenantAttribute = { + ...getMockDeclaredTenantAttribute(), + revocationTimestamp: undefined, + }; + + const declaredAttribute = getMockAttribute( + attributeKind.declared, + validDeclaredTenantAttribute.id + ); + await addOneAttribute(declaredAttribute); + + const validCertifiedTenantAttribute = { + ...getMockCertifiedTenantAttribute(), + revocationTimestamp: undefined, + }; + const certifiedAttribute = getMockAttribute( + attributeKind.certified, + validCertifiedTenantAttribute.id + ); + await addOneAttribute(certifiedAttribute); + + const consumer: Tenant = { + ...getMockTenant(), + selfcareId: generateId(), + attributes: [ + validCertifiedTenantAttribute, + validDeclaredTenantAttribute, + validVerifiedTenantAttribute, + ], + }; + + const delegate: Tenant = getMockTenant(); + + await addOneTenant(consumer); + await addOneTenant(producer); + await addOneTenant(delegate); + + const authData = getRandomAuthData(consumer.id); + + const newPublishedDescriptor: Descriptor = { + ...getMockDescriptorPublished(), + version: "2", + attributes: { + certified: [ + [getMockEServiceAttribute(validCertifiedTenantAttribute.id)], + ], + declared: [[getMockEServiceAttribute(validDeclaredTenantAttribute.id)]], + verified: [[getMockEServiceAttribute(validVerifiedTenantAttribute.id)]], + }, + }; + + const currentDescriptor: Descriptor = { + ...getMockDescriptorPublished(), + state: descriptorState.deprecated, + version: "1", + }; + const eservice: EService = { + ...getMockEService(), + producerId: producer.id, + descriptors: [newPublishedDescriptor, currentDescriptor], + }; + await addOneEService(eservice); + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + state: delegationState.active, + eserviceId: eservice.id, + delegateId: delegate.id, + delegatorId: producer.id, + }); + await addOneDelegation(delegation); + + const agreementId: AgreementId = generateId(); + + const docsNumber = Math.floor(Math.random() * 10) + 1; + const agreementConsumerDocuments = Array.from({ length: docsNumber }, () => + getMockConsumerDocument(agreementId) + ); + + const submitterId = authData.userId; + const activatorId = authData.userId; + const agreement: Agreement = { + ...getMockAgreement( + eservice.id, + consumer.id, + randomArrayItem(agreementUpgradableStates) + ), + id: agreementId, + producerId: eservice.producerId, + descriptorId: currentDescriptor.id, + createdAt: new Date(), + consumerDocuments: agreementConsumerDocuments, + suspendedByConsumer: randomBoolean(), + suspendedByProducer: randomBoolean(), + stamps: { + submission: createStamp(submitterId), + activation: createStamp(activatorId), + suspensionByConsumer: createStamp(authData.userId), + suspensionByProducer: createStamp(authData.userId), + }, + contract: getMockContract(agreementId, consumer.id, producer.id), + suspendedAt: new Date(), + }; + await addOneAgreement(agreement); + + for (const doc of agreementConsumerDocuments) { + await uploadDocument(agreementId, doc.id, doc.name); + } + + vi.spyOn(pdfGenerator, "generate"); + const returnedAgreement = await agreementService.upgradeAgreement( + agreement.id, + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); + const newAgreementId = unsafeBrandId(returnedAgreement.id); + + const actualAgreementArchivedEvent = await readAgreementEventByVersion( + agreement.id, + 1 + ); + + expect(actualAgreementArchivedEvent).toMatchObject({ + type: "AgreementArchivedByUpgrade", + event_version: 2, + version: "1", + stream_id: agreement.id, + }); + + const actualAgreementArchived = decodeProtobufPayload({ + messageType: AgreementArchivedByUpgradeV2, + payload: actualAgreementArchivedEvent.data, + }).agreement; + + const expectedAgreementArchived: Agreement = { + ...agreement, + state: agreementState.archived, + stamps: { + ...agreement.stamps, + archiving: { + delegationId: undefined, + who: authData.userId, + when: new Date(), + }, + }, + }; + + expect(actualAgreementArchived).toEqual( + toAgreementV2(expectedAgreementArchived) + ); + + expect(newAgreementId).toBeDefined(); + + const actualAgreementUpgradedEvent = await readAgreementEventByVersion( + newAgreementId, + 0 + ); + + expect(actualAgreementUpgradedEvent).toMatchObject({ + type: "AgreementUpgraded", + event_version: 2, + version: "0", + stream_id: newAgreementId, + }); + + const actualAgreementUpgraded: Agreement = fromAgreementV2( + decodeProtobufPayload({ + messageType: AgreementUpgradedV2, + payload: actualAgreementUpgradedEvent.data, + }).agreement! + ); + + const contractDocumentId = actualAgreementUpgraded.contract!.id; + const contractCreatedAt = actualAgreementUpgraded.contract!.createdAt; + const contractDocumentName = `${consumer.id}_${ + producer.id + }_${formatDateyyyyMMddHHmmss(contractCreatedAt)}_agreement_contract.pdf`; + + const expectedContract = { + id: contractDocumentId, + contentType: "application/pdf", + createdAt: contractCreatedAt, + path: `${config.agreementContractsPath}/${actualAgreementUpgraded.id}/${contractDocumentId}/${contractDocumentName}`, + prettyName: "Richiesta di fruizione", + name: contractDocumentName, + }; + + const expectedUpgradedAgreement = { + ...agreement, + id: newAgreementId, + descriptorId: newPublishedDescriptor.id, + createdAt: agreement.createdAt, + stamps: { + ...agreement.stamps, + archiving: undefined, + rejection: undefined, + upgrade: { + who: authData.userId, + when: new Date(), + delegationId: delegation.id, + }, + }, + verifiedAttributes: [{ id: validVerifiedTenantAttribute.id }], + certifiedAttributes: [{ id: validCertifiedTenantAttribute.id }], + declaredAttributes: [{ id: validDeclaredTenantAttribute.id }], + consumerDocuments: agreementConsumerDocuments.map( + (doc, i) => ({ + ...doc, + id: actualAgreementUpgraded?.consumerDocuments[i].id, + path: actualAgreementUpgraded?.consumerDocuments[i].path, + }) + ), + contract: expectedContract, + suspendedByPlatform: undefined, + updatedAt: undefined, + }; + + // expect(actualAgreementUpgraded).toEqual(expectedUpgradedAgreement); + expect(actualAgreementUpgraded).toEqual(returnedAgreement); + + expect(submitterId).toEqual(actualAgreementUpgraded.stamps.submission?.who); + expect(activatorId).toEqual(actualAgreementUpgraded.stamps.activation?.who); + + const getIpaCode = (tenant: Tenant): string | undefined => + tenant.externalId.origin === PUBLIC_ADMINISTRATIONS_IDENTIFIER + ? tenant.externalId.value + : undefined; + + const expectedAgreementContractPDFPayload: AgreementContractPDFPayload = { + todayDate: dateAtRomeZone(currentExecutionTime), + todayTime: timeAtRomeZone(currentExecutionTime), + agreementId: newAgreementId, + submitterId, + submissionDate: dateAtRomeZone(currentExecutionTime), + submissionTime: timeAtRomeZone(currentExecutionTime), + activatorId, + activationDate: dateAtRomeZone(currentExecutionTime), + activationTime: timeAtRomeZone(currentExecutionTime), + eserviceName: eservice.name, + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + descriptorVersion: eservice.descriptors[0].version, + producerName: producer.name, + producerIpaCode: getIpaCode(producer), + consumerName: consumer.name, + consumerIpaCode: getIpaCode(consumer), + producerDelegationId: delegation.id, + producerDelegatorName: producer.name, + producerDelegatorIpaCode: getIpaCode(producer), + producerDelegateName: delegate.name, + producerDelegateIpaCode: getIpaCode(delegate), + certifiedAttributes: [ + { + assignmentDate: dateAtRomeZone( + validCertifiedTenantAttribute.assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + validCertifiedTenantAttribute.assignmentTimestamp + ), + attributeName: certifiedAttribute.name, + attributeId: certifiedAttribute.id, + }, + ], + declaredAttributes: [ + { + assignmentDate: dateAtRomeZone( + validDeclaredTenantAttribute.assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + validDeclaredTenantAttribute.assignmentTimestamp + ), + attributeName: declaredAttribute.name, + attributeId: declaredAttribute.id, + }, + ], + verifiedAttributes: [ + { + assignmentDate: dateAtRomeZone( + validVerifiedTenantAttribute.assignmentTimestamp + ), + assignmentTime: timeAtRomeZone( + validVerifiedTenantAttribute.assignmentTimestamp + ), + attributeName: verifiedAttribute.name, + attributeId: verifiedAttribute.id, + expirationDate: undefined, + }, + ], + }; + + expect(pdfGenerator.generate).toHaveBeenCalledWith( + expect.any(String), + expectedAgreementContractPDFPayload + ); + expect(actualAgreementUpgraded).toEqual(returnedAgreement); + + for (const agreementDoc of expectedUpgradedAgreement.consumerDocuments) { + const expectedUploadedDocumentPath = `${config.consumerDocumentsPath}/${newAgreementId}/${agreementDoc.id}/${agreementDoc.name}`; + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContainEqual(expectedUploadedDocumentPath); + } + }); + it("should succeed with invalid Declared or Verified attributes, creating a new Draft agreement", async () => { const producer = getMockTenant(); diff --git a/packages/agreement-process/test/utils.ts b/packages/agreement-process/test/utils.ts index bc82a2fdf7..cee4cbd2e8 100644 --- a/packages/agreement-process/test/utils.ts +++ b/packages/agreement-process/test/utils.ts @@ -27,6 +27,7 @@ import { Attribute, toReadModelAttribute, TenantId, + Delegation, } from "pagopa-interop-models"; import { agreementApi } from "pagopa-interop-api-clients"; import { @@ -39,6 +40,7 @@ import puppeteer, { Browser } from "puppeteer"; import { agreementServiceBuilder } from "../src/services/agreementService.js"; import { readModelServiceBuilder } from "../src/services/readModelService.js"; import { config } from "../src/config/config.js"; +import { contractBuilder } from "../src/services/agreementContractBuilder.js"; export const { cleanup, readModelRepository, postgresDB, fileManager } = await setupTestContainersVitest( @@ -65,11 +67,20 @@ export const agreements = readModelRepository.agreements; export const eservices = readModelRepository.eservices; export const tenants = readModelRepository.tenants; export const attributes = readModelRepository.attributes; +export const delegations = readModelRepository.delegations; export const readModelService = readModelServiceBuilder(readModelRepository); export const pdfGenerator = await initPDFGenerator(); +export const agreementContractBuilder = contractBuilder( + readModelService, + pdfGenerator, + fileManager, + config, + genericLogger +); + export const agreementService = agreementServiceBuilder( postgresDB, readModelService, @@ -110,6 +121,13 @@ export const addOneTenant = async (tenant: Tenant): Promise => { export const addOneAttribute = async (attribute: Attribute): Promise => { await writeInReadmodel(toReadModelAttribute(attribute), attributes); }; + +export const addOneDelegation = async ( + delegation: Delegation +): Promise => { + await writeInReadmodel(delegation, delegations); +}; + export const readLastAgreementEvent = async ( agreementId: AgreementId ): Promise> => diff --git a/packages/api-clients/open-api/agreementApi.yml b/packages/api-clients/open-api/agreementApi.yml index bec3dbe60e..49615171d9 100644 --- a/packages/api-clients/open-api/agreementApi.yml +++ b/packages/api-clients/open-api/agreementApi.yml @@ -1117,6 +1117,9 @@ components: extensionDate: type: string format: date-time + delegationId: + type: string + format: uuid required: - id - verificationDate @@ -1139,6 +1142,9 @@ components: revocationDate: type: string format: date-time + delegationId: + type: string + format: uuid required: - id - verificationDate diff --git a/packages/api-clients/open-api/apiGatewayApi.yml b/packages/api-clients/open-api/apiGatewayApi.yml index 299022dad9..f81280800b 100644 --- a/packages/api-clients/open-api/apiGatewayApi.yml +++ b/packages/api-clients/open-api/apiGatewayApi.yml @@ -2687,6 +2687,7 @@ components: - DEPRECATED - SUSPENDED - ARCHIVED + - WAITING_FOR_APPROVAL EServiceDescriptors: description: eservice descriptors list model type: object diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 70261f707a..3305a787db 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -3885,6 +3885,217 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + /eservices/{eServiceId}/descriptors/{descriptorId}/approve: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + security: + - bearerAuth: [] + tags: + - eservices + summary: approve a delegated new e-service version + operationId: approveDelegatedEServiceDescriptor + parameters: + - name: eServiceId + in: path + description: the eservice id + required: true + schema: + type: string + format: uuid + - name: descriptorId + in: path + description: the descriptor id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: New delegated e-service version approved + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/CreatedResource" + "403": + description: Forbidden + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: EService or Descriptor not found + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "400": + description: Bad request + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /eservices/{eServiceId}/descriptors/{descriptorId}/reject: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + security: + - bearerAuth: [] + tags: + - eservices + summary: reject a delegated new e-service version + operationId: rejectDelegatedEServiceDescriptor + requestBody: + description: Payload containing the reason of the rejection + content: + application/json: + schema: + $ref: "#/components/schemas/RejectDelegatedEServiceDescriptorSeed" + required: true + parameters: + - name: eServiceId + in: path + description: the eservice id + required: true + schema: + type: string + format: uuid + - name: descriptorId + in: path + description: the descriptor id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: New delegated e-service version rejected + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/CreatedResource" + "403": + description: Forbidden + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: EService or Descriptor not found + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "400": + description: Bad request + headers: + "X-Rate-Limit-Limit": + schema: + type: integer + description: Max allowed requests within time interval + "X-Rate-Limit-Remaining": + schema: + type: integer + description: Remaining requests within time interval + "X-Rate-Limit-Interval": + schema: + type: integer + description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /export/eservices/{eserviceId}/descriptors/{descriptorId}: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" @@ -4194,6 +4405,11 @@ paths: format: uuid default: [] explode: false + - in: query + name: delegated + description: if true only delegated e-services will be returned, if false only non-delegated e-services will be returned, if not present all e-services will be returned + schema: + type: boolean - in: query name: offset required: true @@ -7417,6 +7633,19 @@ paths: - tenants operationId: revokeVerifiedAttribute description: Revoke a Verified attribute to a Tenant by the requester Tenant + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + required: + - agreementId + properties: + agreementId: + type: string + format: uuid responses: "204": description: Updated Tenant @@ -8251,6 +8480,15 @@ paths: name: name schema: type: string + - in: query + name: features + description: comma separated feature types to filter the teanants with + schema: + type: array + items: + $ref: "#/components/schemas/TenantFeatureType" + default: [] + explode: false - in: query name: limit required: true @@ -13163,6 +13401,14 @@ components: type: string required: - description + RejectDelegatedEServiceDescriptorSeed: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + required: + - rejectionReason CatalogEServiceDescriptor: type: object additionalProperties: false @@ -13456,6 +13702,10 @@ components: $ref: "#/components/schemas/ProducerDescriptorEService" attributes: $ref: "#/components/schemas/DescriptorAttributes" + rejectionReasons: + type: array + items: + $ref: "#/components/schemas/DescriptorRejectionReason" ProducerDescriptorEService: type: object additionalProperties: false @@ -13519,6 +13769,18 @@ components: properties: prettyName: type: string + DescriptorRejectionReason: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + rejectedAt: + type: string + format: date-time + required: + - rejectionReason + - rejectedAt AgreementApprovalPolicy: type: string description: | @@ -14018,6 +14280,28 @@ components: format: uri required: - url + CompactProducerDescriptor: + type: object + additionalProperties: false + required: + - id + - state + - version + - audience + properties: + id: + type: string + format: uuid + state: + $ref: "#/components/schemas/EServiceDescriptorState" + version: + type: string + audience: + type: array + items: + type: string + requireCorrections: + type: boolean ProducerEService: type: object additionalProperties: false @@ -14034,9 +14318,9 @@ components: mode: $ref: "#/components/schemas/EServiceMode" activeDescriptor: - $ref: "#/components/schemas/CompactDescriptor" + $ref: "#/components/schemas/CompactProducerDescriptor" draftDescriptor: - $ref: "#/components/schemas/CompactDescriptor" + $ref: "#/components/schemas/CompactProducerDescriptor" ProducerEServices: type: object additionalProperties: false @@ -14606,6 +14890,7 @@ components: - DEPRECATED - SUSPENDED - ARCHIVED + - WAITING_FOR_APPROVAL EServiceTechnology: type: string description: EService Descriptor State @@ -14957,6 +15242,11 @@ components: required: - results - pagination + TenantFeatureType: + type: string + enum: + - PERSISTENT_CERTIFIER + - DELEGATED_PRODUCER TenantFeature: oneOf: - type: object @@ -15110,11 +15400,15 @@ components: id: type: string format: uuid + agreementId: + type: string + format: uuid expirationDate: type: string format: date-time required: - id + - agreementId CertifiedTenantAttribute: type: object @@ -15183,6 +15477,9 @@ components: extensionDate: type: string format: date-time + delegationId: + type: string + format: uuid required: - id - verificationDate @@ -15205,6 +15502,10 @@ components: revocationDate: type: string format: date-time + delegationId: + type: string + format: uuid + required: - id - verificationDate @@ -15436,8 +15737,8 @@ components: id: type: string format: uuid - eserviceName: - type: string + eservice: + $ref: "#/components/schemas/CompactEServiceLight" delegate: $ref: "#/components/schemas/DelegationTenant" delegator: diff --git a/packages/api-clients/open-api/catalogApi.yml b/packages/api-clients/open-api/catalogApi.yml index ddd8cbc3a0..0a95620603 100644 --- a/packages/api-clients/open-api/catalogApi.yml +++ b/packages/api-clients/open-api/catalogApi.yml @@ -95,6 +95,11 @@ paths: description: mode schema: $ref: "#/components/schemas/EServiceMode" + - in: query + name: delegated + description: if true only delegated e-services will be returned, if false only non-delegated e-services will be returned, if not present all e-services will be returned + schema: + type: boolean - in: query name: offset required: true @@ -1069,6 +1074,105 @@ paths: application/json: schema: $ref: "#/components/schemas/Problem" + /eservices/:eServiceId/descriptors/:descriptorId/approve: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + security: + - bearerAuth: [] + tags: + - process + summary: approve a delegated new e-service version + operationId: approveDelegatedEServiceDescriptor + parameters: + - name: eServiceId + in: path + description: the eservice id + required: true + schema: + type: string + format: uuid + - name: descriptorId + in: path + description: the descriptor id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: New delegated e-service version approved + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: EService not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "400": + description: Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + /eservices/:eServiceId/descriptors/:descriptorId/reject: + parameters: + - $ref: "#/components/parameters/CorrelationIdHeader" + post: + security: + - bearerAuth: [] + tags: + - process + summary: reject a delegated new e-service version + operationId: rejectDelegatedEServiceDescriptor + requestBody: + description: Payload containing the reason of the rejection + content: + application/json: + schema: + $ref: "#/components/schemas/RejectDelegatedEServiceDescriptorSeed" + required: true + parameters: + - name: eServiceId + in: path + description: the eservice id + required: true + schema: + type: string + format: uuid + - name: descriptorId + in: path + description: the descriptor id + required: true + schema: + type: string + format: uuid + responses: + "204": + description: New delegated e-service version rejected + "403": + description: Forbidden + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "404": + description: EService not found + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" + "400": + description: Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/Problem" /status: get: security: [] @@ -1689,6 +1793,10 @@ components: format: date-time attributes: $ref: "#/components/schemas/Attributes" + rejectionReasons: + type: array + items: + $ref: "#/components/schemas/RejectionReason" Attribute: type: object additionalProperties: false @@ -1740,6 +1848,7 @@ components: - DEPRECATED - SUSPENDED - ARCHIVED + - WAITING_FOR_APPROVAL EServiceDocumentKind: type: string description: EService Document Kind @@ -1757,6 +1866,26 @@ components: - ARCHIVED - MISSING_CERTIFIED_ATTRIBUTES - REJECTED + RejectionReason: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + rejectedAt: + type: string + format: date-time + required: + - rejectionReason + - rejectedAt + RejectDelegatedEServiceDescriptorSeed: + type: object + additionalProperties: false + properties: + rejectionReason: + type: string + required: + - rejectionReason Problem: properties: type: diff --git a/packages/api-clients/open-api/tenantApi.yml b/packages/api-clients/open-api/tenantApi.yml index b82f6cf08c..eb2e187907 100644 --- a/packages/api-clients/open-api/tenantApi.yml +++ b/packages/api-clients/open-api/tenantApi.yml @@ -348,6 +348,15 @@ paths: name: name schema: type: string + - in: query + name: features + description: comma separated feature types to filter the teanants with + schema: + type: array + items: + $ref: "#/components/schemas/TenantFeatureType" + default: [] + explode: false - in: query name: offset required: true @@ -635,6 +644,19 @@ paths: - tenantAttribute operationId: revokeVerifiedAttribute description: Revoke a Verified attribute to a Tenant by the requester Tenant + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + required: + - agreementId + properties: + agreementId: + type: string + format: uuid responses: "200": description: Updated Tenant @@ -1181,6 +1203,11 @@ components: required: - results - totalCount + TenantFeatureType: + type: string + enum: + - PERSISTENT_CERTIFIER + - DELEGATED_PRODUCER TenantFeature: oneOf: - type: object @@ -1329,11 +1356,15 @@ components: id: type: string format: uuid + agreementId: + type: string + format: uuid expirationDate: type: string format: date-time required: - id + - agreementId UpdateVerifiedTenantAttributeSeed: type: object additionalProperties: false @@ -1357,6 +1388,9 @@ components: extensionDate: type: string format: date-time + delegationId: + type: string + format: uuid required: - id - verificationDate @@ -1379,6 +1413,9 @@ components: revocationDate: type: string format: date-time + delegationId: + type: string + format: uuid required: - id - verificationDate diff --git a/packages/api-clients/template-bff.hbs b/packages/api-clients/template-bff.hbs index 9779bf61f5..619c3e02ac 100644 --- a/packages/api-clients/template-bff.hbs +++ b/packages/api-clients/template-bff.hbs @@ -50,6 +50,8 @@ export const {{@key}}Endpoints = makeApi([ schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(PurposeVersionState).optional().default([])), z.array(PurposeVersionState).optional().default([])]) {{else if (and (eq type "Query") (eq schema "z.array(DelegationState).optional().default([])"))}} schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(DelegationState).optional().default([])), z.array(DelegationState).optional().default([])]) + {{else if (and (eq type "Query") (eq schema "z.array(TenantFeatureType).optional().default([])"))}} + schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(TenantFeatureType).optional().default([])), z.array(TenantFeatureType).optional().default([])]) {{else}} schema: {{{schema}}}, {{/if}} diff --git a/packages/api-clients/template.hbs b/packages/api-clients/template.hbs index 04d8e3669b..95ea637934 100644 --- a/packages/api-clients/template.hbs +++ b/packages/api-clients/template.hbs @@ -50,6 +50,8 @@ export const {{@key}}Endpoints = makeApi([ schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(PurposeVersionState).optional().default([])), z.array(PurposeVersionState).optional().default([])]) {{else if (and (eq type "Query") (eq schema "z.array(DelegationState).optional().default([])"))}} schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(DelegationState).optional().default([])), z.array(DelegationState).optional().default([])]) + {{else if (and (eq type "Query") (eq schema "z.array(TenantFeatureType).optional().default([])"))}} + schema: z.union([z.string().optional().transform(v => v ? v.split(",") : undefined ).pipe(z.array(TenantFeatureType).optional().default([])), z.array(TenantFeatureType).optional().default([])]) {{else}} schema: {{{schema}}}, {{/if}} diff --git a/packages/api-gateway/.env b/packages/api-gateway/.env index 0154687f2f..d0459ca36e 100644 --- a/packages/api-gateway/.env +++ b/packages/api-gateway/.env @@ -20,6 +20,7 @@ TENANT_PROCESS_URL="http://localhost:3500" PURPOSE_PROCESS_URL="http://localhost:3400" ATTRIBUTE_REGISTRY_PROCESS_URL="http://localhost:3200" AUTHORIZATION_PROCESS_URL="http://localhost:3300" +DELEGATION_PROCESS_URL="http://localhost:3800" NOTIFIER_URL="http://localhost:9999" READMODEL_DB_HOST=localhost diff --git a/packages/api-gateway/src/clients/clientsProvider.ts b/packages/api-gateway/src/clients/clientsProvider.ts index 02f7bc1f14..83642aab7d 100644 --- a/packages/api-gateway/src/clients/clientsProvider.ts +++ b/packages/api-gateway/src/clients/clientsProvider.ts @@ -6,6 +6,7 @@ import { attributeRegistryApi, notifierApi, authorizationApi, + delegationApi, } from "pagopa-interop-api-clients"; import { config } from "../config/config.js"; @@ -38,6 +39,10 @@ export type AuthorizationProcessClient = { client: ReturnType; }; +export type DelegationProcessClient = ReturnType< + typeof delegationApi.createDelegationApiClient +>; + export type PagoPAInteropBeClients = { catalogProcessClient: CatalogProcessClient; agreementProcessClient: AgreementProcessClient; @@ -46,6 +51,7 @@ export type PagoPAInteropBeClients = { attributeProcessClient: AttributeProcessClient; notifierEventsClient: NotifierEventsClient; authorizationProcessClient: AuthorizationProcessClient; + delegationProcessClient: DelegationProcessClient; }; export function getInteropBeClients(): PagoPAInteropBeClients { @@ -72,5 +78,8 @@ export function getInteropBeClients(): PagoPAInteropBeClients { config.authorizationProcessUrl ), }, + delegationProcessClient: delegationApi.createDelegationApiClient( + config.delegationProcessUrl + ), }; } diff --git a/packages/api-gateway/src/config/config.ts b/packages/api-gateway/src/config/config.ts index 28884e4f04..c65d3e4c78 100644 --- a/packages/api-gateway/src/config/config.ts +++ b/packages/api-gateway/src/config/config.ts @@ -72,6 +72,17 @@ export type AuthorizationProcessServerConfig = z.infer< typeof AuthorizationProcessServerConfig >; +export const DelegationProcessServerConfig = z + .object({ + DELEGATION_PROCESS_URL: APIEndpoint, + }) + .transform((c) => ({ + delegationProcessUrl: c.DELEGATION_PROCESS_URL, + })); +export type DelegationProcessServerConfig = z.infer< + typeof DelegationProcessServerConfig +>; + export const NotifierServerConfig = z .object({ NOTIFIER_URL: APIEndpoint, @@ -97,6 +108,7 @@ const ApiGatewayConfig = CommonHTTPServiceConfig.and(RedisRateLimiterConfig) .and(TenantProcessServerConfig) .and(PurposeProcessServerConfig) .and(AuthorizationProcessServerConfig) + .and(DelegationProcessServerConfig) .and(AttributeRegistryProcessServerConfig) .and(NotifierServerConfig) .and(ReadModelDbConfig); diff --git a/packages/api-gateway/src/models/errors.ts b/packages/api-gateway/src/models/errors.ts index 70fd862e6e..60ad83025d 100644 --- a/packages/api-gateway/src/models/errors.ts +++ b/packages/api-gateway/src/models/errors.ts @@ -28,6 +28,7 @@ export const errorCodes = { tenantAttributeNotFound: "0016", attributeByCodeNotFound: "0017", certifiedAttributeAlreadyAssigned: "0018", + multipleActiveProducerDelegationsForEservice: "0019", }; export type ErrorCodes = keyof typeof errorCodes; @@ -268,3 +269,13 @@ export function certifiedAttributeAlreadyAssigned( title: "Certified attribute already assigned", }); } + +export function multipleActiveProducerDelegationsForEservice( + eserviceId: catalogApi.EService["id"] +): ApiError { + return new ApiError({ + detail: `Unexpected multiple active producer delegations for EService ${eserviceId}`, + code: "multipleActiveProducerDelegationsForEservice", + title: "Multiple active producer delegation found", + }); +} diff --git a/packages/api-gateway/src/routers/apiGatewayRouter.ts b/packages/api-gateway/src/routers/apiGatewayRouter.ts index 0d2973e620..a49151afbb 100644 --- a/packages/api-gateway/src/routers/apiGatewayRouter.ts +++ b/packages/api-gateway/src/routers/apiGatewayRouter.ts @@ -49,6 +49,7 @@ const apiGatewayRouter = ( attributeProcessClient, notifierEventsClient, authorizationProcessClient, + delegationProcessClient, }: PagoPAInteropBeClients ): ZodiosRouter => { const { M2M_ROLE } = userRoles; @@ -71,7 +72,8 @@ const apiGatewayRouter = ( const purposeService = purposeServiceBuilder( purposeProcessClient, catalogProcessClient, - agreementProcessClient + agreementProcessClient, + delegationProcessClient ); const tenantService = tenantServiceBuilder( diff --git a/packages/api-gateway/src/services/purposeService.ts b/packages/api-gateway/src/services/purposeService.ts index 646687db34..be18424bed 100644 --- a/packages/api-gateway/src/services/purposeService.ts +++ b/packages/api-gateway/src/services/purposeService.ts @@ -1,8 +1,14 @@ import { getAllFromPaginated, WithLogger } from "pagopa-interop-commons"; -import { apiGatewayApi, purposeApi } from "pagopa-interop-api-clients"; +import { + apiGatewayApi, + purposeApi, + catalogApi, + delegationApi, +} from "pagopa-interop-api-clients"; import { AgreementProcessClient, CatalogProcessClient, + DelegationProcessClient, PurposeProcessClient, } from "../clients/clientsProvider.js"; import { ApiGatewayAppContext } from "../utilities/context.js"; @@ -14,6 +20,8 @@ import { clientStatusCodeToError } from "../clients/catchClientError.js"; import { purposeNotFound } from "../models/errors.js"; import { assertIsEserviceProducer, + assertIsEserviceDelegateProducer, + assertOnlyOneActiveProducerDelegationForEserviceExists, assertOnlyOneAgreementForEserviceAndConsumerExists, } from "./validators.js"; import { getAllAgreements } from "./agreementService.js"; @@ -61,11 +69,33 @@ const retrievePurpose = async ( }); }); +const retrieveActiveProducerDelegationByEServiceId = async ( + delegationProcessClient: DelegationProcessClient, + headers: ApiGatewayAppContext["headers"], + eserviceId: catalogApi.EService["id"] +): Promise => { + const result = await delegationProcessClient.getDelegations({ + headers, + queries: { + eserviceIds: [eserviceId], + kind: delegationApi.DelegationKind.Values.DELEGATED_PRODUCER, + delegationStates: [delegationApi.DelegationState.Values.ACTIVE], + limit: 1, + offset: 0, + }, + }); + + assertOnlyOneActiveProducerDelegationForEserviceExists(result, eserviceId); + + return result.results.at(0); +}; + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( purposeProcessClient: PurposeProcessClient, catalogProcessClient: CatalogProcessClient, - agreementProcessClient: AgreementProcessClient + agreementProcessClient: AgreementProcessClient, + delegationProcessClient: DelegationProcessClient ) { return { getPurpose: async ( @@ -92,7 +122,18 @@ export function purposeServiceBuilder( }, }); - assertIsEserviceProducer(eservice, organizationId); + try { + assertIsEserviceProducer(eservice, organizationId); + } catch { + const producerDelegation = + await retrieveActiveProducerDelegationByEServiceId( + delegationProcessClient, + headers, + eservice.id + ); + + assertIsEserviceDelegateProducer(producerDelegation, organizationId); + } } return toApiGatewayPurpose(purpose, logger); diff --git a/packages/api-gateway/src/services/validators.ts b/packages/api-gateway/src/services/validators.ts index e77291a705..f484336e66 100644 --- a/packages/api-gateway/src/services/validators.ts +++ b/packages/api-gateway/src/services/validators.ts @@ -3,6 +3,7 @@ import { apiGatewayApi, attributeRegistryApi, catalogApi, + delegationApi, purposeApi, } from "pagopa-interop-api-clients"; import { operationForbidden, TenantId } from "pagopa-interop-models"; @@ -15,6 +16,7 @@ import { missingAvailableDescriptor, multipleAgreementForEserviceAndConsumer, unexpectedDescriptorState, + multipleActiveProducerDelegationsForEservice, } from "../models/errors.js"; import { NonDraftCatalogApiDescriptor } from "../api/catalogApiConverter.js"; @@ -93,3 +95,30 @@ export function assertRegistryAttributeExists( throw attributeNotFoundInRegistry(attributeId); } } + +export function assertIsEserviceDelegateProducer( + producerDelegation: delegationApi.Delegation | undefined, + organizationId: TenantId +): void { + if ( + !producerDelegation || + producerDelegation.kind !== + delegationApi.DelegationKind.Values.DELEGATED_PRODUCER || + producerDelegation.delegateId !== organizationId + ) { + throw operationForbidden; + } +} + +export function assertOnlyOneActiveProducerDelegationForEserviceExists( + producerDelegations: delegationApi.Delegations, + eserviceId: apiGatewayApi.EService["id"] +): void { + if ( + producerDelegations.results.filter( + (d) => d.kind === delegationApi.DelegationKind.Values.DELEGATED_PRODUCER + ).length > 1 + ) { + throw multipleActiveProducerDelegationsForEservice(eserviceId); + } +} diff --git a/packages/authorization-updater/src/index.ts b/packages/authorization-updater/src/index.ts index 0c88769cc6..cec6573999 100644 --- a/packages/authorization-updater/src/index.ts +++ b/packages/authorization-updater/src/index.ts @@ -67,7 +67,8 @@ export async function sendCatalogAuthUpdate( { type: P.union( "EServiceDescriptorPublished", - "EServiceDescriptorActivated" + "EServiceDescriptorActivated", + "EServiceDescriptorDelegatorApproved" ), }, async (msg) => { @@ -134,7 +135,9 @@ export async function sendCatalogAuthUpdate( "EServiceRiskAnalysisAdded", "EServiceRiskAnalysisUpdated", "EServiceRiskAnalysisDeleted", - "EServiceDescriptionUpdated" + "EServiceDescriptionUpdated", + "EServiceDescriptorDelegateSubmitted", + "EServiceDescriptorDelegatorRejected" ), }, () => { diff --git a/packages/backend-for-frontend/src/api/catalogApiConverter.ts b/packages/backend-for-frontend/src/api/catalogApiConverter.ts index a04d60189f..c01042454b 100644 --- a/packages/backend-for-frontend/src/api/catalogApiConverter.ts +++ b/packages/backend-for-frontend/src/api/catalogApiConverter.ts @@ -360,3 +360,20 @@ export function toCompactDescriptor( version: descriptor.version, }; } + +export function toCompactProducerDescriptor( + descriptor: catalogApi.EServiceDescriptor, + isRequesterProducerDelegate: boolean +): bffApi.CompactProducerDescriptor { + return { + id: descriptor.id, + audience: descriptor.audience, + state: descriptor.state, + version: descriptor.version, + requireCorrections: + isRequesterProducerDelegate && + descriptor.state === catalogApi.EServiceDescriptorState.Values.DRAFT && + descriptor.rejectionReasons && + descriptor.rejectionReasons.length > 0, + }; +} diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts index d6c9144c05..0361703baf 100644 --- a/packages/backend-for-frontend/src/api/delegationApiConverter.ts +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -12,6 +12,7 @@ import { } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { toCompactDescriptor } from "./catalogApiConverter.js"; +import { toCompactEserviceLight } from "./agreementApiConverter.js"; export type DelegationsQueryParams = { delegatorIds?: string[]; @@ -95,7 +96,7 @@ export function toBffDelegationApiCompactDelegation( ): bffApi.CompactDelegation { return { id: delegation.id, - eserviceName: eservice.name, + eservice: toCompactEserviceLight(eservice), delegate: { name: delegate.name, id: delegate.id, diff --git a/packages/backend-for-frontend/src/api/tenantApiConverter.ts b/packages/backend-for-frontend/src/api/tenantApiConverter.ts index 772ef3a341..3e764d86aa 100644 --- a/packages/backend-for-frontend/src/api/tenantApiConverter.ts +++ b/packages/backend-for-frontend/src/api/tenantApiConverter.ts @@ -7,6 +7,7 @@ import { isDefined } from "pagopa-interop-commons"; import { CertifiedTenantAttribute, DeclaredTenantAttribute, + DelegationId, Tenant, TenantAttribute, TenantMail, @@ -37,6 +38,9 @@ export function toTenantAttribute( verificationDate: new Date(v.verificationDate), expirationDate: v.expirationDate ? new Date(v.expirationDate) : undefined, extensionDate: v.extensionDate ? new Date(v.extensionDate) : undefined, + delegationId: v.delegationId + ? unsafeBrandId(v.delegationId) + : undefined, })), revokedBy: att.verified.revokedBy.map((r) => ({ id: unsafeBrandId(r.id), @@ -44,6 +48,9 @@ export function toTenantAttribute( revocationDate: new Date(r.revocationDate), expirationDate: r.expirationDate ? new Date(r.expirationDate) : undefined, extensionDate: r.extensionDate ? new Date(r.extensionDate) : undefined, + delegationId: r.delegationId + ? unsafeBrandId(r.delegationId) + : undefined, })), }; diff --git a/packages/backend-for-frontend/src/routers/catalogRouter.ts b/packages/backend-for-frontend/src/routers/catalogRouter.ts index 9a79390e1b..a0779aaaa7 100644 --- a/packages/backend-for-frontend/src/routers/catalogRouter.ts +++ b/packages/backend-for-frontend/src/routers/catalogRouter.ts @@ -79,6 +79,7 @@ const catalogRouter = ( const response = await catalogService.getProducerEServices( req.query.q, req.query.consumersIds, + req.query.delegated, req.query.offset, req.query.limit, ctx @@ -745,7 +746,54 @@ const catalogRouter = ( ); return res.status(errorRes.status).send(errorRes); } - }); + }) + .post( + "/eservices/:eServiceId/descriptors/:descriptorId/approve", + async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + await catalogService.approveDelegatedEServiceDescriptor( + unsafeBrandId(req.params.eServiceId), + unsafeBrandId(req.params.descriptorId), + ctx + ); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error approving eService ${req.params.eServiceId} version ${req.params.descriptorId}` + ); + return res.status(errorRes.status).send(errorRes); + } + } + ) + .post( + "/eservices/:eServiceId/descriptors/:descriptorId/reject", + async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); + try { + await catalogService.rejectDelegatedEServiceDescriptor( + unsafeBrandId(req.params.eServiceId), + unsafeBrandId(req.params.descriptorId), + req.body, + ctx + ); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error rejecting eService ${req.params.eServiceId} version ${req.params.descriptorId}` + ); + return res.status(errorRes.status).send(errorRes); + } + } + ); return catalogRouter; }; diff --git a/packages/backend-for-frontend/src/routers/tenantRouter.ts b/packages/backend-for-frontend/src/routers/tenantRouter.ts index 5f93f610e5..5a9fd981e6 100644 --- a/packages/backend-for-frontend/src/routers/tenantRouter.ts +++ b/packages/backend-for-frontend/src/routers/tenantRouter.ts @@ -6,7 +6,12 @@ import { zodiosValidationErrorToApiProblem, } from "pagopa-interop-commons"; import { bffApi } from "pagopa-interop-api-clients"; -import { AttributeId, TenantId, unsafeBrandId } from "pagopa-interop-models"; +import { + AgreementId, + AttributeId, + TenantId, + unsafeBrandId, +} from "pagopa-interop-models"; import { tenantServiceBuilder } from "../services/tenantService.js"; import { PagoPAInteropBeClients } from "../clients/clientsProvider.js"; import { fromBffAppContext } from "../utilities/context.js"; @@ -308,9 +313,11 @@ const tenantRouter = ( const attributeId = unsafeBrandId( req.params.attributeId ); + const agreementId = unsafeBrandId(req.body.agreementId); await tenantService.revokeVerifiedAttribute( tenantId, attributeId, + agreementId, ctx ); @@ -381,31 +388,28 @@ const tenantRouter = ( return res.status(errorRes.status).send(errorRes); } }) - .get( - "/tenants", - - async (req, res) => { - const ctx = fromBffAppContext(req.ctx, req.headers); + .get("/tenants", async (req, res) => { + const ctx = fromBffAppContext(req.ctx, req.headers); - try { - const result = await tenantService.getTenants( - req.query.name, - req.query.limit, - ctx - ); - return res.status(200).send(bffApi.Tenants.parse(result)); - } catch (error) { - const errorRes = makeApiProblem( - error, - emptyErrorMapper, - ctx.logger, - ctx.correlationId, - `Error retrieving tenants` - ); - return res.status(errorRes.status).send(errorRes); - } + try { + const result = await tenantService.getTenants( + req.query.name, + req.query.features, + req.query.limit, + ctx + ); + return res.status(200).send(bffApi.Tenants.parse(result)); + } catch (error) { + const errorRes = makeApiProblem( + error, + emptyErrorMapper, + ctx.logger, + ctx.correlationId, + `Error retrieving tenants` + ); + return res.status(errorRes.status).send(errorRes); } - ) + }) .post("/tenants/delegatedProducer", async (req, res) => { const ctx = fromBffAppContext(req.ctx, req.headers); const tenantId = ctx.authData.organizationId; diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index 468a8d66dd..48926c86eb 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -50,7 +50,7 @@ import { toCatalogCreateEServiceSeed, toBffCatalogDescriptorEService, toBffCatalogApiEserviceRiskAnalysisSeed, - toCompactDescriptor, + toCompactProducerDescriptor, } from "../api/catalogApiConverter.js"; import { catalogApiDescriptorState, @@ -112,22 +112,31 @@ const enhanceCatalogEService = }; const enhanceProducerEService = ( - eservice: catalogApi.EService + eservice: catalogApi.EService, + requesterId: TenantId ): bffApi.ProducerEService => { const activeDescriptor = getLatestActiveDescriptor(eservice); const draftDescriptor = eservice.descriptors.find( (d) => d.state === catalogApiDescriptorState.DRAFT ); + const isRequesterDelegateProducer = requesterId !== eservice.producerId; + return { id: eservice.id, name: eservice.name, mode: eservice.mode, activeDescriptor: activeDescriptor - ? toCompactDescriptor(activeDescriptor) + ? toCompactProducerDescriptor( + activeDescriptor, + isRequesterDelegateProducer + ) : undefined, draftDescriptor: draftDescriptor - ? toCompactDescriptor(draftDescriptor) + ? toCompactProducerDescriptor( + draftDescriptor, + isRequesterDelegateProducer + ) : undefined, }; }; @@ -429,6 +438,7 @@ export function catalogServiceBuilder( getProducerEServices: async ( eserviceName: string | undefined, consumersIds: string[], + delegated: boolean | undefined, offset: number, limit: number, { headers, authData, logger }: WithLogger @@ -438,7 +448,7 @@ export function catalogServiceBuilder( consumersIds )}` ); - const producerId = authData.organizationId; + const requesterId = authData.organizationId; const res: { results: catalogApi.EService[]; totalCount: number; @@ -453,7 +463,8 @@ export function catalogServiceBuilder( headers, queries: { name: eserviceName, - producersIds: producerId, + producersIds: requesterId, + delegated, offset, limit, }, @@ -466,7 +477,7 @@ export function catalogServiceBuilder( const eserviceIds = ( await getAllAgreements(agreementProcessClient, headers, { consumersIds, - producersIds: [producerId], + producersIds: [requesterId], eservicesIds: [], states: [], }) @@ -478,7 +489,8 @@ export function catalogServiceBuilder( queries: { name: eserviceName, eservicesIds: eserviceIds, - producersIds: producerId, + producersIds: requesterId, + delegated, offset, limit, }, @@ -490,7 +502,9 @@ export function catalogServiceBuilder( } return { - results: res.results.map(enhanceProducerEService), + results: res.results.map((result) => + enhanceProducerEService(result, requesterId) + ), pagination: { offset, limit, @@ -1213,5 +1227,34 @@ export function catalogServiceBuilder( descriptorId: eservice.descriptors[0].id, }; }, + approveDelegatedEServiceDescriptor: async ( + eServiceId: EServiceId, + descriptorId: EServiceId, + { headers, logger }: WithLogger + ): Promise => { + logger.info(`Approving e-service ${eServiceId} version ${descriptorId}`); + await catalogProcessClient.approveDelegatedEServiceDescriptor(undefined, { + headers, + params: { + eServiceId, + descriptorId, + }, + }); + }, + rejectDelegatedEServiceDescriptor: async ( + eServiceId: EServiceId, + descriptorId: EServiceId, + body: catalogApi.RejectDelegatedEServiceDescriptorSeed, + { headers, logger }: WithLogger + ): Promise => { + logger.info(`Rejecting e-service ${eServiceId} version ${descriptorId}`); + await catalogProcessClient.rejectDelegatedEServiceDescriptor(body, { + headers, + params: { + eServiceId, + descriptorId, + }, + }); + }, }; } diff --git a/packages/backend-for-frontend/src/services/tenantService.ts b/packages/backend-for-frontend/src/services/tenantService.ts index 82dac3d5f6..aeaf2edbaf 100644 --- a/packages/backend-for-frontend/src/services/tenantService.ts +++ b/packages/backend-for-frontend/src/services/tenantService.ts @@ -4,7 +4,12 @@ import { tenantApi, } from "pagopa-interop-api-clients"; import { isDefined, WithLogger } from "pagopa-interop-commons"; -import { AttributeId, CorrelationId, TenantId } from "pagopa-interop-models"; +import { + AgreementId, + AttributeId, + CorrelationId, + TenantId, +} from "pagopa-interop-models"; import { AttributeProcessClient, SelfcareV2Client, @@ -107,6 +112,7 @@ export function tenantServiceBuilder( }, async getTenants( name: string | undefined, + features: tenantApi.TenantFeatureType[] | undefined, limit: number, { logger, headers, correlationId }: WithLogger ): Promise { @@ -115,6 +121,7 @@ export function tenantServiceBuilder( const pagedResults = await tenantProcessClient.tenant.getTenants({ queries: { name, + features, limit, offset, }, @@ -383,13 +390,14 @@ export function tenantServiceBuilder( async revokeVerifiedAttribute( tenantId: TenantId, attributeId: AttributeId, + agreementId: AgreementId, { logger, headers }: WithLogger ): Promise { logger.info( `Revoking verified attribute ${attributeId} for tenant ${tenantId}` ); await tenantProcessClient.tenantAttribute.revokeVerifiedAttribute( - undefined, + { agreementId }, { params: { tenantId, attributeId }, headers, diff --git a/packages/backend-for-frontend/src/services/validators.ts b/packages/backend-for-frontend/src/services/validators.ts index 1e201fff48..24f82f4820 100644 --- a/packages/backend-for-frontend/src/services/validators.ts +++ b/packages/backend-for-frontend/src/services/validators.ts @@ -55,7 +55,7 @@ export async function assertNotDelegatedEservice( delegationProcessClient, headers, { - kind: toDelegationKind(delegationKind.delegatedConsumer), + kind: toDelegationKind(delegationKind.delegatedProducer), delegatorIds: [delegatorId], eserviceIds: [eserviceid], delegationStates: [ diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 5e81c6a5db..25ff33e1b5 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.10", + "@pagopa/interop-outbound-models": "1.0.11a", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} \ No newline at end of file +} diff --git a/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts index 9b282048ef..62d5d9be92 100644 --- a/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts @@ -98,6 +98,9 @@ export function toOutboundEventV2( { type: "EServiceDescriptorPublished" }, { type: "EServiceDescriptorSuspended" }, { type: "EServiceDraftDescriptorDeleted" }, + { type: "EServiceDescriptorDelegateSubmitted" }, + { type: "EServiceDescriptorDelegatorApproved" }, + { type: "EServiceDescriptorDelegatorRejected" }, (msg) => ({ event_version: msg.event_version, type: msg.type, diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts index 441744646e..e8a9044cc2 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts @@ -139,8 +139,11 @@ export async function handleMessageV1( dynamoDBClient ); }) - .with(descriptorState.draft, descriptorState.deprecated, () => - Promise.resolve() + .with( + descriptorState.draft, + descriptorState.deprecated, + descriptorState.waitingForApproval, + () => Promise.resolve() ) .exhaustive(); }) diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index 68da3e38a7..3faf84e6b6 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -31,95 +31,99 @@ export async function handleMessageV2( dynamoDBClient: DynamoDBClient ): Promise { await match(message) - .with({ type: "EServiceDescriptorPublished" }, async (msg) => { - const { eservice, descriptor } = parseEServiceAndDescriptor( - msg.data.eservice, - unsafeBrandId(msg.data.descriptorId), - message.type - ); - const previousDescriptor = eservice.descriptors.find( - (d) => d.version === (Number(descriptor.version) - 1).toString() - ); - - // flow for current descriptor - const processCurrentDescriptor = async (): Promise => { - const primaryKeyCurrent = makePlatformStatesEServiceDescriptorPK({ - eserviceId: eservice.id, - descriptorId: descriptor.id, - }); - const existingCatalogEntryCurrent = await readCatalogEntry( - primaryKeyCurrent, - dynamoDBClient + .with( + { type: "EServiceDescriptorPublished" }, + { type: "EServiceDescriptorDelegatorApproved" }, + async (msg) => { + const { eservice, descriptor } = parseEServiceAndDescriptor( + msg.data.eservice, + unsafeBrandId(msg.data.descriptorId), + message.type ); - if (existingCatalogEntryCurrent) { - if (existingCatalogEntryCurrent.version > msg.version) { - // Stops processing if the message is older than the catalog entry - return Promise.resolve(); - } else { - await updateDescriptorStateInPlatformStatesEntry( - dynamoDBClient, - primaryKeyCurrent, - descriptorStateToItemState(descriptor.state), - msg.version - ); - } - } else { - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCurrent, - state: descriptorStateToItemState(descriptor.state), - descriptorAudience: descriptor.audience, - descriptorVoucherLifespan: descriptor.voucherLifespan, - version: msg.version, - updatedAt: new Date().toISOString(), - }; - - await writeCatalogEntry(catalogEntry, dynamoDBClient); - } - - // token-generation-states - const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: eservice.id, - descriptorId: descriptor.id, - }); - await updateDescriptorInfoInTokenGenerationStatesTable( - eserviceId_descriptorId, - descriptorStateToItemState(descriptor.state), - descriptor.voucherLifespan, - descriptor.audience, - dynamoDBClient + const previousDescriptor = eservice.descriptors.find( + (d) => d.version === (Number(descriptor.version) - 1).toString() ); - }; - await processCurrentDescriptor(); + // flow for current descriptor + const processCurrentDescriptor = async (): Promise => { + const primaryKeyCurrent = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const existingCatalogEntryCurrent = await readCatalogEntry( + primaryKeyCurrent, + dynamoDBClient + ); + if (existingCatalogEntryCurrent) { + if (existingCatalogEntryCurrent.version > msg.version) { + // Stops processing if the message is older than the catalog entry + return Promise.resolve(); + } else { + await updateDescriptorStateInPlatformStatesEntry( + dynamoDBClient, + primaryKeyCurrent, + descriptorStateToItemState(descriptor.state), + msg.version + ); + } + } else { + const catalogEntry: PlatformStatesCatalogEntry = { + PK: primaryKeyCurrent, + state: descriptorStateToItemState(descriptor.state), + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: msg.version, + updatedAt: new Date().toISOString(), + }; - // flow for previous descriptor + await writeCatalogEntry(catalogEntry, dynamoDBClient); + } - if ( - !previousDescriptor || - previousDescriptor.state !== descriptorState.archived - ) { - return Promise.resolve(); - } else { - const primaryKeyPrevious = makePlatformStatesEServiceDescriptorPK({ - eserviceId: eservice.id, - descriptorId: previousDescriptor.id, - }); + // token-generation-states + const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + await updateDescriptorInfoInTokenGenerationStatesTable( + eserviceId_descriptorId, + descriptorStateToItemState(descriptor.state), + descriptor.voucherLifespan, + descriptor.audience, + dynamoDBClient + ); + }; - await deleteCatalogEntry(primaryKeyPrevious, dynamoDBClient); + await processCurrentDescriptor(); - // token-generation-states - const eserviceId_descriptorId_previous = - makeGSIPKEServiceIdDescriptorId({ + // flow for previous descriptor + + if ( + !previousDescriptor || + previousDescriptor.state !== descriptorState.archived + ) { + return Promise.resolve(); + } else { + const primaryKeyPrevious = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, descriptorId: previousDescriptor.id, }); - await updateDescriptorStateInTokenGenerationStatesTable( - eserviceId_descriptorId_previous, - descriptorStateToItemState(previousDescriptor.state), - dynamoDBClient - ); + + await deleteCatalogEntry(primaryKeyPrevious, dynamoDBClient); + + // token-generation-states + const eserviceId_descriptorId_previous = + makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: previousDescriptor.id, + }); + await updateDescriptorStateInTokenGenerationStatesTable( + eserviceId_descriptorId_previous, + descriptorStateToItemState(previousDescriptor.state), + dynamoDBClient + ); + } } - }) + ) .with( { type: "EServiceDescriptorActivated" }, { type: "EServiceDescriptorSuspended" }, @@ -239,6 +243,8 @@ export async function handleMessageV2( { type: "EServiceRiskAnalysisUpdated" }, { type: "EServiceRiskAnalysisDeleted" }, { type: "EServiceDescriptionUpdated" }, + { type: "EServiceDescriptorDelegatorRejected" }, + { type: "EServiceDescriptorDelegateSubmitted" }, () => Promise.resolve() ) .exhaustive(); diff --git a/packages/catalog-process/src/model/domain/apiConverter.ts b/packages/catalog-process/src/model/domain/apiConverter.ts index 70e0d2a969..061230b697 100644 --- a/packages/catalog-process/src/model/domain/apiConverter.ts +++ b/packages/catalog-process/src/model/domain/apiConverter.ts @@ -43,6 +43,7 @@ export function descriptorStateToApiEServiceDescriptorState( .with(descriptorState.suspended, () => "SUSPENDED") .with(descriptorState.deprecated, () => "DEPRECATED") .with(descriptorState.archived, () => "ARCHIVED") + .with(descriptorState.waitingForApproval, () => "WAITING_FOR_APPROVAL") .exhaustive(); } @@ -55,6 +56,7 @@ export function apiDescriptorStateToDescriptorState( .with("SUSPENDED", () => descriptorState.suspended) .with("DEPRECATED", () => descriptorState.deprecated) .with("ARCHIVED", () => descriptorState.archived) + .with("WAITING_FOR_APPROVAL", () => descriptorState.waitingForApproval) .exhaustive(); } @@ -172,6 +174,10 @@ export const descriptorToApiDescriptor = ( declared: descriptor.attributes.declared, verified: descriptor.attributes.verified, }, + rejectionReasons: descriptor.rejectionReasons?.map((reason) => ({ + rejectionReason: reason.rejectionReason, + rejectedAt: reason.rejectedAt.toJSON(), + })), }); export const eServiceToApiEService = ( diff --git a/packages/catalog-process/src/model/domain/errors.ts b/packages/catalog-process/src/model/domain/errors.ts index a183c27716..57bc54eaff 100644 --- a/packages/catalog-process/src/model/domain/errors.ts +++ b/packages/catalog-process/src/model/domain/errors.ts @@ -1,5 +1,7 @@ +import { RiskAnalysisValidationIssue } from "pagopa-interop-commons"; import { ApiError, + DelegationId, DescriptorId, EServiceDocumentId, EServiceId, @@ -7,7 +9,6 @@ import { TenantId, makeApiProblemBuilder, } from "pagopa-interop-models"; -import { RiskAnalysisValidationIssue } from "pagopa-interop-commons"; export const errorCodes = { eServiceDescriptorNotFound: "0001", @@ -33,6 +34,7 @@ export const errorCodes = { riskAnalysisDuplicated: "0021", eserviceWithoutValidDescriptors: "0022", audienceCannotBeEmpty: "0023", + eserviceWithActiveOrPendingDelegation: "0024", }; export type ErrorCodes = keyof typeof errorCodes; @@ -89,7 +91,7 @@ export function eServiceDocumentNotFound( }); } -export function notValidDescriptor( +export function notValidDescriptorState( descriptorId: DescriptorId, descriptorStatus: string ): ApiError { @@ -271,3 +273,14 @@ export function audienceCannotBeEmpty( title: "Audience cannot be empty", }); } + +export function eserviceWithActiveOrPendingDelegation( + eserviceId: EServiceId, + delegationId: DelegationId +): ApiError { + return new ApiError({ + detail: `E-service ${eserviceId} can't be deleted with an active or pending delegation ${delegationId}`, + code: "eserviceWithActiveOrPendingDelegation", + title: "E-service with active or pending delegation", + }); +} diff --git a/packages/catalog-process/src/model/domain/models.ts b/packages/catalog-process/src/model/domain/models.ts index 3e87abb3fe..6e4b215458 100644 --- a/packages/catalog-process/src/model/domain/models.ts +++ b/packages/catalog-process/src/model/domain/models.ts @@ -21,6 +21,7 @@ export type ApiGetEServicesFilters = { agreementStates: AgreementState[]; name?: string; mode?: EServiceMode; + delegated?: boolean; }; export type EServiceDocument = { diff --git a/packages/catalog-process/src/model/domain/toEvent.ts b/packages/catalog-process/src/model/domain/toEvent.ts index e9db5c3e79..62dc47aa92 100644 --- a/packages/catalog-process/src/model/domain/toEvent.ts +++ b/packages/catalog-process/src/model/domain/toEvent.ts @@ -479,3 +479,60 @@ export const toCreateEventEServiceDescriptionUpdated = ( }, correlationId, }); + +export const toCreateEventEServiceDescriptorDelegateSubmitted = ( + version: number, + descriptorId: DescriptorId, + eservice: EService, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: eservice.id, + version, + event: { + type: "EServiceDescriptorDelegateSubmitted", + event_version: 2, + data: { + descriptorId, + eservice: toEServiceV2(eservice), + }, + }, + correlationId, +}); + +export const toCreateEventEServiceDescriptorDelegatorApproved = ( + version: number, + descriptorId: DescriptorId, + eservice: EService, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: eservice.id, + version, + event: { + type: "EServiceDescriptorDelegatorApproved", + event_version: 2, + data: { + descriptorId, + eservice: toEServiceV2(eservice), + }, + }, + correlationId, +}); + +export const toCreateEventEServiceDescriptorDelegatorRejected = ( + version: number, + descriptorId: DescriptorId, + eservice: EService, + correlationId: CorrelationId +): CreateEvent => ({ + streamId: eservice.id, + version, + event: { + type: "EServiceDescriptorDelegatorRejected", + event_version: 2, + data: { + descriptorId, + eservice: toEServiceV2(eservice), + }, + }, + correlationId, +}); diff --git a/packages/catalog-process/src/routers/EServiceRouter.ts b/packages/catalog-process/src/routers/EServiceRouter.ts index addca3936e..015b266b1e 100644 --- a/packages/catalog-process/src/routers/EServiceRouter.ts +++ b/packages/catalog-process/src/routers/EServiceRouter.ts @@ -54,6 +54,8 @@ import { updateEServiceDescriptionErrorMapper, updateEServiceErrorMapper, updateRiskAnalysisErrorMapper, + approveDelegatedEServiceDescriptorErrorMapper, + rejectDelegatedEServiceDescriptorErrorMapper, } from "../utilities/errorMappers.js"; const readModelService = readModelServiceBuilder( @@ -110,6 +112,7 @@ const eservicesRouter = ( states, agreementStates, mode, + delegated, offset, limit, } = req.query; @@ -126,6 +129,7 @@ const eservicesRouter = ( ), name, mode: mode ? apiEServiceModeToEServiceMode(mode) : undefined, + delegated, }, offset, limit, @@ -761,6 +765,55 @@ const eservicesRouter = ( return res.status(errorRes.status).send(errorRes); } } + ) + .post( + "/eservices/:eServiceId/descriptors/:descriptorId/approve", + authorizationMiddleware([ADMIN_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + await catalogService.approveDelegatedEServiceDescriptor( + unsafeBrandId(req.params.eServiceId), + unsafeBrandId(req.params.descriptorId), + ctx + ); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + approveDelegatedEServiceDescriptorErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } + ) + .post( + "/eservices/:eServiceId/descriptors/:descriptorId/reject", + authorizationMiddleware([ADMIN_ROLE, API_ROLE]), + async (req, res) => { + const ctx = fromAppContext(req.ctx); + + try { + await catalogService.rejectDelegatedEServiceDescriptor( + unsafeBrandId(req.params.eServiceId), + unsafeBrandId(req.params.descriptorId), + req.body, + ctx + ); + return res.status(204).send(); + } catch (error) { + const errorRes = makeApiProblem( + error, + rejectDelegatedEServiceDescriptorErrorMapper, + ctx.logger, + ctx.correlationId + ); + return res.status(errorRes.status).send(errorRes); + } + } ); return eservicesRouter; }; diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 66a63aef03..4fb707964a 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -1,48 +1,73 @@ /* eslint-disable max-params */ +import { catalogApi } from "pagopa-interop-api-clients"; import { + AppContext, AuthData, DB, FileManager, Logger, WithLogger, - AppContext, eventRepository, + formatDateddMMyyyyHHmmss, hasPermission, riskAnalysisValidatedFormToNewRiskAnalysis, riskAnalysisValidatedFormToNewRiskAnalysisForm, userRoles, - formatDateddMMyyyyHHmmss, } from "pagopa-interop-commons"; import { + AttributeId, + Delegation, Descriptor, DescriptorId, + DescriptorRejectionReason, DescriptorState, Document, EService, EServiceDocumentId, EServiceId, + EserviceAttributes, + ListResult, + RiskAnalysis, + RiskAnalysisId, + Tenant, TenantId, WithMetadata, + agreementState, catalogEventToBinaryData, + delegationKind, + delegationState, descriptorState, + eserviceMode, generateId, + operationForbidden, unsafeBrandId, - ListResult, - AttributeId, - agreementState, - EserviceAttributes, - Tenant, - RiskAnalysis, - RiskAnalysisId, - eserviceMode, } from "pagopa-interop-models"; -import { catalogApi } from "pagopa-interop-api-clients"; import { match } from "ts-pattern"; +import { RejectDelegatedEServiceDescriptorSeed } from "../../../api-clients/dist/catalogApi.js"; +import { config } from "../config/config.js"; import { apiAgreementApprovalPolicyToAgreementApprovalPolicy, apiEServiceModeToEServiceMode, apiTechnologyToTechnology, } from "../model/domain/apiConverter.js"; +import { + attributeNotFound, + audienceCannotBeEmpty, + eServiceDescriptorNotFound, + eServiceDescriptorWithoutInterface, + eServiceDocumentNotFound, + eServiceDuplicate, + eServiceNotFound, + eServiceRiskAnalysisNotFound, + eserviceWithoutValidDescriptors, + inconsistentDailyCalls, + interfaceAlreadyExists, + notValidDescriptorState, + originNotCompliant, + prettyNameDuplicate, + riskAnalysisDuplicated, + tenantNotFound, +} from "../model/domain/errors.js"; import { ApiGetEServicesFilters, Consumer } from "../model/domain/models.js"; import { toCreateEventClonedEServiceAdded, @@ -52,6 +77,9 @@ import { toCreateEventEServiceDescriptorActivated, toCreateEventEServiceDescriptorAdded, toCreateEventEServiceDescriptorArchived, + toCreateEventEServiceDescriptorDelegateSubmitted, + toCreateEventEServiceDescriptorDelegatorApproved, + toCreateEventEServiceDescriptorDelegatorRejected, toCreateEventEServiceDescriptorPublished, toCreateEventEServiceDescriptorQuotasUpdated, toCreateEventEServiceDescriptorSuspended, @@ -68,37 +96,20 @@ import { toCreateEventEServiceRiskAnalysisUpdated, toCreateEventEServiceUpdated, } from "../model/domain/toEvent.js"; -import { config } from "../config/config.js"; import { nextDescriptorVersion } from "../utilities/versionGenerator.js"; -import { - eServiceDescriptorNotFound, - eServiceDocumentNotFound, - eServiceDuplicate, - notValidDescriptor, - eServiceNotFound, - eServiceDescriptorWithoutInterface, - interfaceAlreadyExists, - attributeNotFound, - inconsistentDailyCalls, - originNotCompliant, - tenantNotFound, - eServiceRiskAnalysisNotFound, - prettyNameDuplicate, - riskAnalysisDuplicated, - eserviceWithoutValidDescriptors, - audienceCannotBeEmpty, -} from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; import { - assertRequesterAllowed, + assertDocumentDeletableDescriptorState, + assertHasNoDraftOrWaitingForApprovalDescriptor, + assertInterfaceDeletableDescriptorState, assertIsDraftEservice, assertIsReceiveEservice, + assertNoExistingProducerDelegationInActiveOrPendingState, + assertRequesterIsDelegateProducerOrProducer, + assertRequesterIsProducer, + assertRiskAnalysisIsValidForPublication, assertTenantKindExists, validateRiskAnalysisSchemaOrThrow, - assertHasNoDraftDescriptor, - assertRiskAnalysisIsValidForPublication, - assertInterfaceDeletableDescriptorState, - assertDocumentDeletableDescriptorState, } from "./validators.js"; const retrieveEService = async ( @@ -167,6 +178,33 @@ const retrieveRiskAnalysis = ( return riskAnalysis; }; +const assertRequesterCanPublish = ( + producerDelegation: Delegation | undefined, + eservice: EService, + authData: AuthData +): void => { + if (producerDelegation) { + if ( + producerDelegation.kind !== delegationKind.delegatedProducer || + authData.organizationId !== producerDelegation.delegateId + ) { + throw operationForbidden; + } + } else { + assertRequesterIsProducer(eservice.producerId, authData); + } +}; + +const retrieveActiveProducerDelegation = async ( + eservice: EService, + readModelService: ReadModelService +): Promise => + await readModelService.getLatestDelegation({ + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + states: [delegationState.active], + }); + const updateDescriptorState = ( descriptor: Descriptor, newState: DescriptorState @@ -174,11 +212,15 @@ const updateDescriptorState = ( const descriptorStateChange = [descriptor.state, newState]; return match(descriptorStateChange) - .with([descriptorState.draft, descriptorState.published], () => ({ - ...descriptor, - state: newState, - publishedAt: new Date(), - })) + .with( + [descriptorState.draft, descriptorState.published], + [descriptorState.waitingForApproval, descriptorState.published], + () => ({ + ...descriptor, + state: newState, + publishedAt: new Date(), + }) + ) .with([descriptorState.published, descriptorState.suspended], () => ({ ...descriptor, state: newState, @@ -335,7 +377,11 @@ export function catalogServiceBuilder( logger.info(`Retrieving EService ${eserviceId}`); const eservice = await retrieveEService(eserviceId, readModelService); - return applyVisibilityToEService(eservice.data, authData); + return await applyVisibilityToEService( + eservice.data, + authData, + readModelService + ); }, async getEServices( @@ -355,8 +401,10 @@ export function catalogServiceBuilder( limit ); - const eservicesToReturn = eservicesList.results.map((eservice) => - applyVisibilityToEService(eservice, authData) + const eservicesToReturn = await Promise.all( + eservicesList.results.map((eservice) => + applyVisibilityToEService(eservice, authData, readModelService) + ) ); return { @@ -397,9 +445,10 @@ export function catalogServiceBuilder( const eservice = await retrieveEService(eserviceId, readModelService); const descriptor = retrieveDescriptor(descriptorId, eservice); const document = retrieveDocument(eserviceId, descriptor, documentId); - const checkedEService = applyVisibilityToEService( + const checkedEService = await applyVisibilityToEService( eservice.data, - authData + authData, + readModelService ); if (!checkedEService.descriptors.find((d) => d.id === descriptorId)) { throw eServiceDocumentNotFound(eserviceId, descriptorId, documentId); @@ -504,7 +553,12 @@ export function catalogServiceBuilder( logger.info(`Updating EService ${eserviceId}`); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); assertIsDraftEservice(eservice.data); @@ -512,7 +566,7 @@ export function catalogServiceBuilder( const eserviceWithSameName = await readModelService.getEServiceByNameAndProducerId({ name: eserviceSeed.name, - producerId: authData.organizationId, + producerId: eservice.data.producerId, }); if (eserviceWithSameName !== undefined) { throw eServiceDuplicate(eserviceSeed.name); @@ -549,7 +603,6 @@ export function catalogServiceBuilder( description: eserviceSeed.description, name: eserviceSeed.name, technology: updatedTechnology, - producerId: authData.organizationId, mode: updatedMode, riskAnalysis: checkedRiskAnalysis, descriptors: interfaceHasToBeDeleted @@ -580,10 +633,14 @@ export function catalogServiceBuilder( logger.info(`Deleting EService ${eserviceId}`); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); - + assertRequesterIsProducer(eservice.data.producerId, authData); assertIsDraftEservice(eservice.data); + await assertNoExistingProducerDelegationInActiveOrPendingState( + eserviceId, + readModelService + ); + if (eservice.data.descriptors.length === 0) { const eserviceDeletionEvent = toCreateEventEServiceDeleted( eservice.metadata.version, @@ -636,7 +693,12 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); @@ -646,7 +708,7 @@ export function catalogServiceBuilder( descriptor.state !== descriptorState.published && descriptor.state !== descriptorState.suspended ) { - throw notValidDescriptor(descriptor.id, descriptor.state); + throw notValidDescriptorState(descriptor.id, descriptor.state); } if (document.kind === "INTERFACE" && descriptor.interface !== undefined) { @@ -726,7 +788,12 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); const document = retrieveDocument(eserviceId, descriptor, documentId); @@ -793,7 +860,12 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); @@ -803,7 +875,7 @@ export function catalogServiceBuilder( descriptor.state !== descriptorState.published && descriptor.state !== descriptorState.suspended ) { - throw notValidDescriptor(descriptor.id, descriptor.state); + throw notValidDescriptorState(descriptor.id, descriptor.state); } const document = retrieveDocument(eserviceId, descriptor, documentId); @@ -877,8 +949,13 @@ export function catalogServiceBuilder( logger.info(`Creating Descriptor for EService ${eserviceId}`); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); - assertHasNoDraftDescriptor(eservice.data); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); + assertHasNoDraftOrWaitingForApprovalDescriptor(eservice.data); const newVersion = nextDescriptorVersion(eservice.data); @@ -988,12 +1065,17 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); if (descriptor.state !== descriptorState.draft) { - throw notValidDescriptor(descriptorId, descriptor.state); + throw notValidDescriptorState(descriptorId, descriptor.state); } await deleteDescriptorInterfaceAndDocs(descriptor, fileManager, logger); @@ -1039,12 +1121,20 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); if (descriptor.state !== descriptorState.draft) { - throw notValidDescriptor(descriptorId, descriptor.state.toString()); + throw notValidDescriptorState( + descriptorId, + descriptor.state.toString() + ); } if (seed.dailyCallsPerConsumer > seed.dailyCallsTotal) { @@ -1098,11 +1188,20 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + + const producerDelegation = await retrieveActiveProducerDelegation( + eservice.data, + readModelService + ); + + assertRequesterCanPublish(producerDelegation, eservice.data, authData); const descriptor = retrieveDescriptor(descriptorId, eservice); if (descriptor.state !== descriptorState.draft) { - throw notValidDescriptor(descriptor.id, descriptor.state.toString()); + throw notValidDescriptorState( + descriptor.id, + descriptor.state.toString() + ); } if (descriptor.interface === undefined) { @@ -1122,71 +1221,37 @@ export function catalogServiceBuilder( throw audienceCannotBeEmpty(descriptor.id); } - const currentActiveDescriptor = eservice.data.descriptors.find( - (d: Descriptor) => d.state === descriptorState.published - ); - - const publishedDescriptor = updateDescriptorState( - descriptor, - descriptorState.published - ); - - const eserviceWithPublishedDescriptor = replaceDescriptor( - eservice.data, - publishedDescriptor - ); - - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - const event = async () => { - if (currentActiveDescriptor !== undefined) { - const agreements = await readModelService.listAgreements({ - eservicesIds: [eserviceId], - consumersIds: [], - producersIds: [], - states: [agreementState.active, agreementState.suspended], - limit: 1, - descriptorId: currentActiveDescriptor.id, - }); - if (agreements.length === 0) { - const eserviceWithArchivedAndPublishedDescriptors = - replaceDescriptor( - eserviceWithPublishedDescriptor, - archiveDescriptor(eserviceId, currentActiveDescriptor, logger) - ); - - return toCreateEventEServiceDescriptorPublished( - eserviceId, - eservice.metadata.version, - descriptorId, - eserviceWithArchivedAndPublishedDescriptors, - correlationId - ); - } else { - const eserviceWithDeprecatedAndPublishedDescriptors = - replaceDescriptor( - eserviceWithPublishedDescriptor, - deprecateDescriptor(eserviceId, currentActiveDescriptor, logger) - ); + if (producerDelegation) { + const eserviceWithWaitingForApprovalDescriptor = replaceDescriptor( + eservice.data, + updateDescriptorState(descriptor, descriptorState.waitingForApproval) + ); + await repository.createEvent( + toCreateEventEServiceDescriptorDelegateSubmitted( + eservice.metadata.version, + descriptor.id, + eserviceWithWaitingForApprovalDescriptor, + correlationId + ) + ); + } else { + const updatedEService = await processDescriptorPublication( + eservice.data, + descriptor, + readModelService, + logger + ); - return toCreateEventEServiceDescriptorPublished( - eserviceId, - eservice.metadata.version, - descriptorId, - eserviceWithDeprecatedAndPublishedDescriptors, - correlationId - ); - } - } else { - return toCreateEventEServiceDescriptorPublished( + await repository.createEvent( + toCreateEventEServiceDescriptorPublished( eserviceId, eservice.metadata.version, descriptorId, - eserviceWithPublishedDescriptor, + updatedEService, correlationId - ); - } - }; - await repository.createEvent(await event()); + ) + ); + } }, async suspendDescriptor( @@ -1199,14 +1264,22 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); if ( descriptor.state !== descriptorState.deprecated && descriptor.state !== descriptorState.published ) { - throw notValidDescriptor(descriptorId, descriptor.state.toString()); + throw notValidDescriptorState( + descriptorId, + descriptor.state.toString() + ); } const updatedDescriptor = updateDescriptorState( @@ -1236,11 +1309,19 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); if (descriptor.state !== descriptorState.suspended) { - throw notValidDescriptor(descriptorId, descriptor.state.toString()); + throw notValidDescriptorState( + descriptorId, + descriptor.state.toString() + ); } const updatedDescriptor = updateDescriptorState( @@ -1305,7 +1386,11 @@ export function catalogServiceBuilder( const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + assertRequesterIsProducer(eservice.data.producerId, authData); + await assertNoExistingProducerDelegationInActiveOrPendingState( + eservice.data.id, + readModelService + ); const clonedEServiceName = `${ eservice.data.name @@ -1314,7 +1399,7 @@ export function catalogServiceBuilder( if ( await readModelService.getEServiceByNameAndProducerId({ name: clonedEServiceName, - producerId: authData.organizationId, + producerId: eservice.data.producerId, }) ) { throw eServiceDuplicate(clonedEServiceName); @@ -1419,7 +1504,12 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); const updatedDescriptor = updateDescriptorState( @@ -1450,7 +1540,12 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); @@ -1459,7 +1554,10 @@ export function catalogServiceBuilder( descriptor.state !== descriptorState.suspended && descriptor.state !== descriptorState.deprecated ) { - throw notValidDescriptor(descriptorId, descriptor.state.toString()); + throw notValidDescriptorState( + descriptorId, + descriptor.state.toString() + ); } if (seed.dailyCallsPerConsumer > seed.dailyCallsTotal) { @@ -1498,12 +1596,17 @@ export function catalogServiceBuilder( const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); assertIsDraftEservice(eservice.data); assertIsReceiveEservice(eservice.data); const tenant = await retrieveTenant( - authData.organizationId, + eservice.data.producerId, readModelService ); assertTenantKindExists(tenant); @@ -1558,12 +1661,17 @@ export function catalogServiceBuilder( const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); assertIsDraftEservice(eservice.data); assertIsReceiveEservice(eservice.data); const tenant = await retrieveTenant( - authData.organizationId, + eservice.data.producerId, readModelService ); assertTenantKindExists(tenant); @@ -1624,7 +1732,12 @@ export function catalogServiceBuilder( ); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); assertIsDraftEservice(eservice.data); assertIsReceiveEservice(eservice.data); @@ -1655,11 +1768,17 @@ export function catalogServiceBuilder( logger.info(`Updating EService ${eserviceId} description`); const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eservice.data.id, + authData, + readModelService + ); const hasValidDescriptor = eservice.data.descriptors.some( (descriptor) => descriptor.state !== descriptorState.draft && + descriptor.state !== descriptorState.waitingForApproval && descriptor.state !== descriptorState.archived ); if (!hasValidDescriptor) { @@ -1680,30 +1799,136 @@ export function catalogServiceBuilder( ); return updatedEservice; }, + async approveDelegatedEServiceDescriptor( + eserviceId: EServiceId, + descriptorId: DescriptorId, + { authData, correlationId, logger }: WithLogger + ): Promise { + logger.info(`Approving EService ${eserviceId} version ${descriptorId}`); + const eservice = await retrieveEService(eserviceId, readModelService); + + assertRequesterIsProducer(eservice.data.producerId, authData); + + const descriptor = retrieveDescriptor(descriptorId, eservice); + + if (descriptor.state !== descriptorState.waitingForApproval) { + throw notValidDescriptorState( + descriptor.id, + descriptor.state.toString() + ); + } + + const updatedEService = await processDescriptorPublication( + eservice.data, + descriptor, + readModelService, + logger + ); + + await repository.createEvent( + toCreateEventEServiceDescriptorDelegatorApproved( + eservice.metadata.version, + descriptor.id, + updatedEService, + correlationId + ) + ); + }, + async rejectDelegatedEServiceDescriptor( + eserviceId: EServiceId, + descriptorId: DescriptorId, + body: RejectDelegatedEServiceDescriptorSeed, + { authData, correlationId, logger }: WithLogger + ): Promise { + logger.info(`Rejecting EService ${eserviceId} version ${descriptorId}`); + const eservice = await retrieveEService(eserviceId, readModelService); + + assertRequesterIsProducer(eservice.data.producerId, authData); + + const descriptor = retrieveDescriptor(descriptorId, eservice); + + if (descriptor.state !== descriptorState.waitingForApproval) { + throw notValidDescriptorState( + descriptor.id, + descriptor.state.toString() + ); + } + + const newRejectionReason: DescriptorRejectionReason = { + rejectionReason: body.rejectionReason, + rejectedAt: new Date(), + }; + + const updatedDescriptor = updateDescriptorState( + { + ...descriptor, + rejectionReasons: [ + ...(descriptor.rejectionReasons ?? []), + newRejectionReason, + ], + }, + descriptorState.draft + ); + + const updatedEService = replaceDescriptor( + eservice.data, + updatedDescriptor + ); + + await repository.createEvent( + toCreateEventEServiceDescriptorDelegatorRejected( + eservice.metadata.version, + descriptor.id, + updatedEService, + correlationId + ) + ); + }, }; } -const isUserAllowedToSeeDraft = ( +async function isUserAllowedToSeeDraft( + eservice: EService, authData: AuthData, - producerId: TenantId -): boolean => - hasPermission( - [userRoles.ADMIN_ROLE, userRoles.API_ROLE, userRoles.SUPPORT_ROLE], - authData - ) && authData.organizationId === producerId; - -const applyVisibilityToEService = ( + readModelService: ReadModelService +): Promise { + if ( + !hasPermission( + [userRoles.ADMIN_ROLE, userRoles.API_ROLE, userRoles.SUPPORT_ROLE], + authData + ) + ) { + return false; + } + + if (authData.organizationId === eservice.producerId) { + return true; + } + + const activeProducerDelegation = await readModelService.getLatestDelegation({ + eserviceId: eservice.id, + delegateId: authData.organizationId, + kind: delegationKind.delegatedProducer, + states: [delegationState.active], + }); + + return activeProducerDelegation !== undefined; +} + +async function applyVisibilityToEService( eservice: EService, - authData: AuthData -): EService => { - if (isUserAllowedToSeeDraft(authData, eservice.producerId)) { + authData: AuthData, + readModelService: ReadModelService +): Promise { + if (await isUserAllowedToSeeDraft(eservice, authData, readModelService)) { return eservice; } if ( eservice.descriptors.length === 0 || (eservice.descriptors.length === 1 && - eservice.descriptors[0].state === descriptorState.draft) + (eservice.descriptors[0].state === descriptorState.draft || + eservice.descriptors[0].state === descriptorState.waitingForApproval)) ) { throw eServiceNotFound(eservice.id); } @@ -1711,10 +1936,12 @@ const applyVisibilityToEService = ( return { ...eservice, descriptors: eservice.descriptors.filter( - (d) => d.state !== descriptorState.draft + (d) => + d.state !== descriptorState.draft && + d.state !== descriptorState.waitingForApproval ), }; -}; +} const deleteDescriptorInterfaceAndDocs = async ( descriptor: Descriptor, @@ -1733,4 +1960,45 @@ const deleteDescriptorInterfaceAndDocs = async ( await Promise.all(deleteDescriptorDocs); }; +const processDescriptorPublication = async ( + eservice: EService, + descriptor: Descriptor, + readModelService: ReadModelService, + logger: Logger +): Promise => { + const currentActiveDescriptor = eservice.descriptors.find( + (d: Descriptor) => d.state === descriptorState.published + ); + + const publishedDescriptor = updateDescriptorState( + descriptor, + descriptorState.published + ); + + const eserviceWithPublishedDescriptor = replaceDescriptor( + eservice, + publishedDescriptor + ); + + if (!currentActiveDescriptor) { + return eserviceWithPublishedDescriptor; + } + + const currentEServiceAgreements = await readModelService.listAgreements({ + eservicesIds: [eservice.id], + consumersIds: [], + producersIds: [], + states: [agreementState.active, agreementState.suspended], + limit: 1, + descriptorId: currentActiveDescriptor.id, + }); + + return replaceDescriptor( + eserviceWithPublishedDescriptor, + currentEServiceAgreements.length === 0 + ? archiveDescriptor(eservice.id, currentActiveDescriptor, logger) + : deprecateDescriptor(eservice.id, currentActiveDescriptor, logger) + ); +}; + export type CatalogService = ReturnType; diff --git a/packages/catalog-process/src/services/readModelService.ts b/packages/catalog-process/src/services/readModelService.ts index b7f6e02bcb..73bb0212d5 100644 --- a/packages/catalog-process/src/services/readModelService.ts +++ b/packages/catalog-process/src/services/readModelService.ts @@ -27,6 +27,11 @@ import { EServiceReadModel, TenantReadModel, genericInternalError, + Delegation, + DelegationState, + delegationState, + delegationKind, + DelegationKind, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { z } from "zod"; @@ -99,6 +104,7 @@ export function readModelServiceBuilder( const agreements = readModelRepository.agreements; const attributes = readModelRepository.attributes; const tenants = readModelRepository.tenants; + const delegations = readModelRepository.delegations; return { async getEServices( @@ -115,6 +121,7 @@ export function readModelServiceBuilder( name, attributesIds, mode, + delegated, } = filters; const ids = await match(agreementStates.length) .with(0, () => eservicesIds) @@ -147,10 +154,35 @@ export function readModelServiceBuilder( "data.id": { $in: ids }, }); - const producersIdsFilter: ReadModelFilter = - ReadModelRepository.arrayToFilter(producersIds, { - "data.producerId": { $in: producersIds }, - }); + const delegationLookup = + producersIds.length > 0 || delegated !== undefined + ? [ + { + $lookup: { + from: "delegations", + localField: "data.id", + foreignField: "data.eserviceId", + as: "delegations", + }, + }, + ] + : []; + + const producersIdsFilter = ReadModelRepository.arrayToFilter( + producersIds, + { + $or: [ + { "data.producerId": { $in: producersIds } }, + { + "delegations.data.delegateId": { $in: producersIds }, + "delegations.data.state": { $eq: delegationState.active }, + "delegations.data.kind": { + $eq: delegationKind.delegatedProducer, + }, + }, + ], + } + ); const descriptorsStateFilter: ReadModelFilter = ReadModelRepository.arrayToFilter(states, { @@ -201,7 +233,12 @@ export function readModelServiceBuilder( { "data.producerId": { $ne: authData.organizationId } }, { "data.descriptors": { $size: 1 } }, { - "data.descriptors.state": { $eq: descriptorState.draft }, + "data.descriptors.state": { + $in: [ + descriptorState.draft, + descriptorState.waitingForApproval, + ], + }, }, ], }, @@ -214,7 +251,12 @@ export function readModelServiceBuilder( $and: [ { "data.descriptors": { $size: 1 } }, { - "data.descriptors.state": { $eq: descriptorState.draft }, + "data.descriptors.state": { + $in: [ + descriptorState.draft, + descriptorState.waitingForApproval, + ], + }, }, ], }, @@ -225,18 +267,40 @@ export function readModelServiceBuilder( ? { "data.mode": { $eq: mode } } : {}; + const delegatedFilter: ReadModelFilter = match(delegated) + .with(true, () => ({ + "delegations.data.state": { + $in: [delegationState.active, delegationState.waitingForApproval], + }, + "delegations.data.kind": delegationKind.delegatedProducer, + })) + .with(false, () => ({ + delegations: { + $not: { + $elemMatch: { + "data.state": { + $in: [ + delegationState.active, + delegationState.waitingForApproval, + ], + }, + "data.kind": delegationKind.delegatedProducer, + }, + }, + }, + })) + .otherwise(() => ({})); + const aggregationPipeline = [ - { - $match: { - ...nameFilter, - ...idsFilter, - ...producersIdsFilter, - ...descriptorsStateFilter, - ...attributesFilter, - ...visibilityFilter, - ...modeFilter, - } satisfies ReadModelFilter, - }, + ...delegationLookup, + { $match: nameFilter }, + { $match: idsFilter }, + { $match: producersIdsFilter }, + { $match: descriptorsStateFilter }, + { $match: attributesFilter }, + { $match: visibilityFilter }, + { $match: modeFilter }, + { $match: delegatedFilter }, { $project: { data: 1, @@ -498,6 +562,46 @@ export function readModelServiceBuilder( async getTenantById(id: TenantId): Promise { return getTenant(tenants, { "data.id": id }); }, + + async getLatestDelegation({ + eserviceId, + states, + kind, + delegateId, + }: { + eserviceId: EServiceId; + states: DelegationState[]; + kind: DelegationKind; + delegateId?: TenantId; + }): Promise { + const data = await delegations.findOne( + { + "data.eserviceId": eserviceId, + "data.kind": kind, + ...(states.length > 0 ? { "data.state": { $in: states } } : {}), + ...(delegateId ? { "data.delegateId": delegateId } : {}), + }, + { + projection: { data: true }, + sort: { "data.createdAt": -1 }, + } + ); + + if (!data) { + return undefined; + } + const result = Delegation.safeParse(data.data); + + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + + return result.data; + }, }; } diff --git a/packages/catalog-process/src/services/validators.ts b/packages/catalog-process/src/services/validators.ts index e09b961d62..7beef7f529 100644 --- a/packages/catalog-process/src/services/validators.ts +++ b/packages/catalog-process/src/services/validators.ts @@ -1,3 +1,4 @@ +import { catalogApi } from "pagopa-interop-api-clients"; import { AuthData, RiskAnalysisValidatedForm, @@ -5,40 +6,93 @@ import { validateRiskAnalysis, } from "pagopa-interop-commons"; import { - TenantId, - operationForbidden, + Descriptor, EService, - descriptorState, - eserviceMode, + EServiceId, Tenant, + TenantId, TenantKind, - Descriptor, + delegationKind, + delegationState, + descriptorState, + eserviceMode, + operationForbidden, } from "pagopa-interop-models"; -import { catalogApi } from "pagopa-interop-api-clients"; import { match } from "ts-pattern"; import { - eserviceNotInDraftState, - eserviceNotInReceiveMode, - tenantKindNotFound, - riskAnalysisValidationFailed, draftDescriptorAlreadyExists, eServiceRiskAnalysisIsRequired, + eserviceNotInDraftState, + eserviceNotInReceiveMode, + eserviceWithActiveOrPendingDelegation, + notValidDescriptorState, riskAnalysisNotValid, - notValidDescriptor, + riskAnalysisValidationFailed, + tenantKindNotFound, } from "../model/domain/errors.js"; +import { ReadModelService } from "./readModelService.js"; -export function assertRequesterAllowed( +export async function assertRequesterIsDelegateProducerOrProducer( + producerId: TenantId, + eserviceId: EServiceId, + authData: AuthData, + readModelService: ReadModelService +): Promise { + if (authData.userRoles.includes("internal")) { + return; + } + + // Search for active producer delegation + const producerDelegation = await readModelService.getLatestDelegation({ + eserviceId, + kind: delegationKind.delegatedProducer, + states: [delegationState.active], + }); + + // If an active producer delegation exists, check if the requester is the delegate + if (producerDelegation) { + const isRequesterDelegateProducer = + authData.organizationId === producerDelegation.delegateId; + + if (!isRequesterDelegateProducer) { + throw operationForbidden; + } + } else { + // If no active producer delegation exists, ensure the requester is the producer + assertRequesterIsProducer(producerId, authData); + } +} + +export function assertRequesterIsProducer( producerId: TenantId, authData: AuthData ): void { - if ( - !authData.userRoles.includes("internal") && - producerId !== authData.organizationId - ) { + if (authData.userRoles.includes("internal")) { + return; + } + if (producerId !== authData.organizationId) { throw operationForbidden; } } +export async function assertNoExistingProducerDelegationInActiveOrPendingState( + eserviceId: EServiceId, + readModelService: ReadModelService +): Promise { + const producerDelegation = await readModelService.getLatestDelegation({ + eserviceId, + kind: delegationKind.delegatedProducer, + states: [delegationState.active, delegationState.waitingForApproval], + }); + + if (producerDelegation) { + throw eserviceWithActiveOrPendingDelegation( + eserviceId, + producerDelegation.id + ); + } +} + export function assertIsDraftEservice(eservice: EService): void { if (eservice.descriptors.some((d) => d.state !== descriptorState.draft)) { throw eserviceNotInDraftState(eservice.id); @@ -59,11 +113,15 @@ export function assertTenantKindExists( } } -export function assertHasNoDraftDescriptor(eservice: EService): void { - const hasDraftDescriptor = eservice.descriptors.some( - (d: Descriptor) => d.state === descriptorState.draft +export function assertHasNoDraftOrWaitingForApprovalDescriptor( + eservice: EService +): void { + const hasInvalidDescriptor = eservice.descriptors.some( + (d: Descriptor) => + d.state === descriptorState.draft || + d.state === descriptorState.waitingForApproval ); - if (hasDraftDescriptor) { + if (hasInvalidDescriptor) { throw draftDescriptorAlreadyExists(eservice.id); } } @@ -113,8 +171,9 @@ export function assertInterfaceDeletableDescriptorState( descriptorState.deprecated, descriptorState.published, descriptorState.suspended, + descriptorState.waitingForApproval, () => { - throw notValidDescriptor(descriptor.id, descriptor.state); + throw notValidDescriptorState(descriptor.id, descriptor.state); } ) .exhaustive(); @@ -129,10 +188,11 @@ export function assertDocumentDeletableDescriptorState( descriptorState.deprecated, descriptorState.published, descriptorState.suspended, + descriptorState.waitingForApproval, () => void 0 ) .with(descriptorState.archived, () => { - throw notValidDescriptor(descriptor.id, descriptor.state); + throw notValidDescriptorState(descriptor.id, descriptor.state); }) .exhaustive(); } diff --git a/packages/catalog-process/src/utilities/errorMappers.ts b/packages/catalog-process/src/utilities/errorMappers.ts index a667f1b13d..718c4eb7e7 100644 --- a/packages/catalog-process/src/utilities/errorMappers.ts +++ b/packages/catalog-process/src/utilities/errorMappers.ts @@ -295,3 +295,21 @@ export const updateEServiceDescriptionErrorMapper = ( .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) .with("eserviceWithoutValidDescriptors", () => HTTP_STATUS_CONFLICT) .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const approveDelegatedEServiceDescriptorErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("eServiceNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("eServiceDescriptorNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); + +export const rejectDelegatedEServiceDescriptorErrorMapper = ( + error: ApiError +): number => + match(error.code) + .with("eServiceNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("eServiceDescriptorNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("operationForbidden", () => HTTP_STATUS_FORBIDDEN) + .otherwise(() => HTTP_STATUS_INTERNAL_SERVER_ERROR); diff --git a/packages/catalog-process/test/activateDescriptor.test.ts b/packages/catalog-process/test/activateDescriptor.test.ts index 4bccaad41b..5c190e9fc4 100644 --- a/packages/catalog-process/test/activateDescriptor.test.ts +++ b/packages/catalog-process/test/activateDescriptor.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -8,13 +11,15 @@ import { EServiceDescriptorActivatedV2, toEServiceV2, operationForbidden, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, } from "../src/model/domain/errors.js"; import { addOneEService, @@ -24,6 +29,7 @@ import { getMockEService, getMockDescriptor, getMockDocument, + addOneDelegation, } from "./utils.js"; describe("activate descriptor", () => { @@ -48,7 +54,7 @@ describe("activate descriptor", () => { logger: genericLogger, }); - const updatedDescriptor = { + const expectedDescriptor = { ...descriptor, state: descriptorState.published, }; @@ -65,7 +71,61 @@ describe("activate descriptor", () => { const expectedEservice = toEServiceV2({ ...eservice, - descriptors: [updatedDescriptor], + descriptors: [expectedDescriptor], + }); + expect(writtenPayload.eservice).toEqual(expectedEservice); + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + }); + + it("should write on event-store for the activation of a descriptor (delegate)", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + interface: mockDocument, + state: descriptorState.suspended, + }; + + const delegate = getMockAuthData(); + + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: delegate.organizationId, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.activateDescriptor(eservice.id, descriptor.id, { + authData: delegate, + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const expectedDescriptor = { + ...descriptor, + state: descriptorState.published, + }; + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceDescriptorActivated"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorActivatedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [expectedDescriptor], }); expect(writtenPayload.eservice).toEqual(expectedEservice); expect(writtenPayload.descriptorId).toEqual(descriptor.id); @@ -122,94 +182,61 @@ describe("activate descriptor", () => { ).rejects.toThrowError(operationForbidden); }); - it("should throw notValidDescriptor if the descriptor is in draft state", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - state: descriptorState.draft, - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - await addOneEService(eservice); - expect( - catalogService.activateDescriptor(mockEService.id, mockDescriptor.id, { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - }) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.draft) - ); - }); - - it("should throw notValidDescriptor if the descriptor is in published state", async () => { + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, - state: descriptorState.published, + state: descriptorState.suspended, }; const eservice: EService = { ...mockEService, descriptors: [descriptor], }; - await addOneEService(eservice); - expect( - catalogService.activateDescriptor(mockEService.id, mockDescriptor.id, { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - }) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.published) - ); - }); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); - it("should throw notValidDescriptor if the descriptor is in deprecated state", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - interface: mockDocument, - state: descriptorState.deprecated, - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; await addOneEService(eservice); - expect( - catalogService.activateDescriptor(mockEService.id, mockDescriptor.id, { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - }) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.deprecated) - ); - }); + await addOneDelegation(delegation); - it("should throw notValidDescriptor if the descriptor is in archived state", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - interface: mockDocument, - state: descriptorState.archived, - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - await addOneEService(eservice); expect( - catalogService.activateDescriptor(mockEService.id, mockDescriptor.id, { + catalogService.activateDescriptor(eservice.id, descriptor.id, { authData: getMockAuthData(eservice.producerId), correlationId: generateId(), serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.archived) - ); + ).rejects.toThrowError(operationForbidden); }); + + it.each([ + descriptorState.draft, + descriptorState.published, + descriptorState.deprecated, + descriptorState.archived, + descriptorState.waitingForApproval, + ])( + "should throw notValidDescriptorState if the descriptor is in state %s", + async (state) => { + const descriptor: Descriptor = { + ...mockDescriptor, + state, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + expect( + catalogService.activateDescriptor(mockEService.id, mockDescriptor.id, { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); + } + ); }); diff --git a/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts b/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts new file mode 100644 index 0000000000..ed06263ca1 --- /dev/null +++ b/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts @@ -0,0 +1,362 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { genericLogger } from "pagopa-interop-commons"; +import { + decodeProtobufPayload, + getMockTenant, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; +import { + Descriptor, + descriptorState, + EService, + toEServiceV2, + Tenant, + generateId, + operationForbidden, + delegationState, + EServiceDescriptorDelegatorApprovedV2, + delegationKind, +} from "pagopa-interop-models"; +import { beforeAll, vi, afterAll, expect, describe, it } from "vitest"; +import { + eServiceNotFound, + eServiceDescriptorNotFound, + notValidDescriptorState, +} from "../src/model/domain/errors.js"; +import { + addOneEService, + catalogService, + getMockAuthData, + readLastEserviceEvent, + addOneTenant, + addOneAgreement, + getMockEService, + getMockDescriptor, + getMockDocument, + getMockAgreement, + addOneDelegation, +} from "./utils.js"; + +describe("publish descriptor", () => { + const mockEService = getMockEService(); + const mockDescriptor = getMockDescriptor(); + const mockDocument = getMockDocument(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + afterAll(() => { + vi.useRealTimers(); + }); + it("should write on event-store for the publication of a waiting for approval descriptor", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.waitingForApproval, + interface: mockDocument, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + await catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorDelegatorApproved", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDelegatorApprovedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + publishedAt: new Date(), + state: descriptorState.published, + }, + ], + }); + + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + }); + + it("should also archive the previously published descriptor", async () => { + const descriptor1: Descriptor = { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + publishedAt: new Date(), + interface: mockDocument, + }; + const descriptor2: Descriptor = { + ...mockDescriptor, + id: generateId(), + state: descriptorState.waitingForApproval, + interface: mockDocument, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor1, descriptor2], + }; + await addOneEService(eservice); + await catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + descriptor2.id, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorDelegatorApproved", + event_version: 2, + }); + + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDelegatorApprovedV2, + payload: writtenEvent.data, + }); + + const updatedDescriptor1: Descriptor = { + ...descriptor1, + archivedAt: new Date(), + state: descriptorState.archived, + }; + const updatedDescriptor2: Descriptor = { + ...descriptor2, + publishedAt: new Date(), + state: descriptorState.published, + }; + + const expectedEservice: EService = { + ...eservice, + descriptors: [updatedDescriptor1, updatedDescriptor2], + }; + expect(writtenPayload).toEqual({ + eservice: toEServiceV2(expectedEservice), + descriptorId: descriptor2.id, + }); + }); + + it("should also write deprecate the previously published descriptor if there was a valid agreement", async () => { + const descriptor1: Descriptor = { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + publishedAt: new Date(), + interface: mockDocument, + }; + const descriptor2: Descriptor = { + ...mockDescriptor, + id: generateId(), + state: descriptorState.waitingForApproval, + interface: mockDocument, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor1, descriptor2], + }; + await addOneEService(eservice); + const tenant: Tenant = { + ...getMockTenant(), + }; + await addOneTenant(tenant); + const agreement = getMockAgreement({ + eserviceId: eservice.id, + descriptorId: descriptor1.id, + producerId: eservice.producerId, + consumerId: tenant.id, + }); + await addOneAgreement(agreement); + await catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + descriptor2.id, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorDelegatorApproved", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDelegatorApprovedV2, + payload: writtenEvent.data, + }); + + const updatedDescriptor1: Descriptor = { + ...descriptor1, + deprecatedAt: new Date(), + state: descriptorState.deprecated, + }; + const updatedDescriptor2: Descriptor = { + ...descriptor2, + publishedAt: new Date(), + state: descriptorState.published, + }; + + const expectedEservice: EService = { + ...eservice, + descriptors: [updatedDescriptor1, updatedDescriptor2], + }; + expect(writtenPayload).toEqual({ + eservice: toEServiceV2(expectedEservice), + descriptorId: descriptor2.id, + }); + }); + + it("should throw eServiceNotFound if the eService doesn't exist", async () => { + await expect( + catalogService.approveDelegatedEServiceDescriptor( + mockEService.id, + mockDescriptor.id, + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(eServiceNotFound(mockEService.id)); + }); + + it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { + const eservice: EService = { + ...mockEService, + descriptors: [], + }; + await addOneEService(eservice); + expect( + catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + mockDescriptor.id, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError( + eServiceDescriptorNotFound(eservice.id, mockDescriptor.id) + ); + }); + + it("should throw operationForbidden if the requester is not the producer", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + expect( + catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + { + authData: getMockAuthData(), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + + it("should throw operationForbidden if the requester is the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + + it.each( + Object.values(descriptorState).filter( + (s) => s !== descriptorState.waitingForApproval + ) + )( + "should throw notValidDescriptorState if the descriptor is in %s state", + async (state) => { + const descriptor: Descriptor = { + ...mockDescriptor, + interface: mockDocument, + state, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + expect( + catalogService.approveDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); + } + ); +}); diff --git a/packages/catalog-process/test/archiveDescriptor.test.ts b/packages/catalog-process/test/archiveDescriptor.test.ts index 9b56a416e5..955f9221cd 100644 --- a/packages/catalog-process/test/archiveDescriptor.test.ts +++ b/packages/catalog-process/test/archiveDescriptor.test.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -9,7 +12,9 @@ import { EServiceDescriptorActivatedV2, toEServiceV2, operationForbidden, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { @@ -17,6 +22,7 @@ import { eServiceDescriptorNotFound, } from "../src/model/domain/errors.js"; import { + addOneDelegation, addOneEService, catalogService, getMockAuthData, @@ -58,7 +64,62 @@ describe("archive descriptor", () => { payload: writtenEvent.data, }); - const updatedDescriptor = { + const expectedDescriptor = { + ...descriptor, + state: descriptorState.archived, + archivedAt: new Date( + Number(writtenPayload.eservice!.descriptors[0]!.archivedAt) + ), + }; + + const expectedEService = toEServiceV2({ + ...eservice, + descriptors: [expectedDescriptor], + }); + expect(writtenPayload.eservice).toEqual(expectedEService); + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + }); + + it("should write on event-store for the archiving of a descriptor (delegate)", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + interface: mockDocument, + state: descriptorState.suspended, + }; + const delegate = getMockAuthData(); + + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: delegate.organizationId, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.archiveDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(delegate.organizationId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceDescriptorArchived"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorActivatedV2, + payload: writtenEvent.data, + }); + + const expectedDescriptor = { ...descriptor, state: descriptorState.archived, archivedAt: new Date( @@ -68,7 +129,7 @@ describe("archive descriptor", () => { const expectedEService = toEServiceV2({ ...eservice, - descriptors: [updatedDescriptor], + descriptors: [expectedDescriptor], }); expect(writtenPayload.eservice).toEqual(expectedEService); expect(writtenPayload.descriptorId).toEqual(descriptor.id); @@ -123,4 +184,32 @@ describe("archive descriptor", () => { }) ).rejects.toThrowError(operationForbidden); }); + + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.archiveDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); }); diff --git a/packages/catalog-process/test/cloneDescriptor.test.ts b/packages/catalog-process/test/cloneDescriptor.test.ts index 5031a026b6..dd754228a5 100644 --- a/packages/catalog-process/test/cloneDescriptor.test.ts +++ b/packages/catalog-process/test/cloneDescriptor.test.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger, FileManagerError } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -12,6 +15,8 @@ import { toEServiceV2, generateId, operationForbidden, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { beforeAll, vi, afterAll, expect, describe, it } from "vitest"; import { formatDateddMMyyyyHHmmss } from "pagopa-interop-commons"; @@ -30,6 +35,7 @@ import { getMockEService, getMockDescriptor, getMockDocument, + addOneDelegation, } from "./utils.js"; describe("clone descriptor", () => { @@ -314,6 +320,33 @@ describe("clone descriptor", () => { }) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester is a producer delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.cloneDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { const eservice: EService = { ...mockEService, diff --git a/packages/catalog-process/test/createDescriptor.test.ts b/packages/catalog-process/test/createDescriptor.test.ts index bf74a79e47..410233dabf 100644 --- a/packages/catalog-process/test/createDescriptor.test.ts +++ b/packages/catalog-process/test/createDescriptor.test.ts @@ -3,6 +3,7 @@ import { genericLogger } from "pagopa-interop-commons"; import { decodeProtobufPayload, + getMockDelegation, readEventByStreamIdAndVersion, } from "pagopa-interop-commons-test"; import { @@ -15,6 +16,8 @@ import { descriptorState, operationForbidden, EServiceDescriptorDocumentAddedV2, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { catalogApi } from "pagopa-interop-api-clients"; @@ -35,6 +38,7 @@ import { getMockEService, buildCreateDescriptorSeed, postgresDB, + addOneDelegation, } from "./utils.js"; describe("create descriptor", async () => { @@ -248,31 +252,159 @@ describe("create descriptor", async () => { }); }); - it("should throw draftDescriptorAlreadyExists if a draft descriptor already exists", async () => { - const descriptor: Descriptor = { + it("should write on event-store for the creation of a descriptor (delegate)", async () => { + const mockDocument = getMockDocument(); + const existingDescriptor: Descriptor = { + ...getMockDescriptor(), + interface: getMockDocument(), + state: descriptorState.published, + }; + const mockDescriptor: Descriptor = { ...getMockDescriptor(), - state: descriptorState.draft, + docs: [mockDocument], }; const eservice: EService = { ...getMockEService(), - descriptors: [descriptor], + descriptors: [existingDescriptor], }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); await addOneEService(eservice); - expect( - catalogService.createDescriptor( - eservice.id, - buildCreateDescriptorSeed(descriptor), - { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - } - ) - ).rejects.toThrowError(draftDescriptorAlreadyExists(eservice.id)); + await addOneDelegation(delegation); + + const attribute: Attribute = { + name: "Attribute name", + id: generateId(), + kind: "Declared", + description: "Attribute Description", + creationTime: new Date(), + }; + await addOneAttribute(attribute); + const descriptorSeed: catalogApi.EServiceDescriptorSeed = { + ...buildCreateDescriptorSeed(mockDescriptor), + attributes: { + certified: [], + declared: [ + [{ id: attribute.id, explicitAttributeVerification: false }], + ], + verified: [], + }, + }; + + const returnedDescriptor = await catalogService.createDescriptor( + eservice.id, + descriptorSeed, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const newDescriptorId = returnedDescriptor.id; + const descriptorCreationEvent = await readEventByStreamIdAndVersion( + eservice.id, + 1, + "catalog", + postgresDB + ); + const documentAdditionEvent = await readLastEserviceEvent(eservice.id); + + expect(descriptorCreationEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorAdded", + event_version: 2, + }); + expect(documentAdditionEvent).toMatchObject({ + stream_id: eservice.id, + version: "2", + type: "EServiceDescriptorDocumentAdded", + event_version: 2, + }); + + const descriptorCreationPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorAddedV2, + payload: descriptorCreationEvent.data, + }); + const documentAdditionPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDocumentAddedV2, + payload: documentAdditionEvent.data, + }); + + const newDescriptor: Descriptor = { + ...mockDescriptor, + version: "2", + createdAt: new Date(), + id: newDescriptorId, + serverUrls: [], + attributes: { + certified: [], + declared: [ + [{ id: attribute.id, explicitAttributeVerification: false }], + ], + verified: [], + }, + docs: [], + }; + + const expectedEserviceAfterDescriptorCreation: EService = { + ...eservice, + descriptors: [...eservice.descriptors, newDescriptor], + }; + const expectedEserviceAfterDocumentAddition: EService = { + ...expectedEserviceAfterDescriptorCreation, + descriptors: expectedEserviceAfterDescriptorCreation.descriptors.map( + (d) => + d.id === newDescriptor.id + ? { ...newDescriptor, docs: [mockDocument] } + : d + ), + }; + + expect(descriptorCreationPayload).toEqual({ + descriptorId: newDescriptorId, + eservice: toEServiceV2(expectedEserviceAfterDescriptorCreation), + }); + expect(documentAdditionPayload).toEqual({ + documentId: mockDocument.id, + descriptorId: newDescriptorId, + eservice: toEServiceV2(expectedEserviceAfterDocumentAddition), + }); }); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should throw draftDescriptorAlreadyExists if a descriptor with state %s already exists", + async (state) => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + await addOneEService(eservice); + expect( + catalogService.createDescriptor( + eservice.id, + buildCreateDescriptorSeed(descriptor), + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(draftDescriptorAlreadyExists(eservice.id)); + } + ); + it("should throw eServiceNotFound if the eservice doesn't exist", async () => { const mockEService = getMockEService(); expect( @@ -359,6 +491,38 @@ describe("create descriptor", async () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + interface: getMockDocument(), + state: descriptorState.published, + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.createDescriptor( + eservice.id, + buildCreateDescriptorSeed(descriptor), + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("should throw inconsistentDailyCalls if dailyCallsPerConsumer is greater than dailyCallsTotal", async () => { const descriptorSeed: catalogApi.EServiceDescriptorSeed = { ...buildCreateDescriptorSeed(getMockDescriptor()), diff --git a/packages/catalog-process/test/createRiskAnalysis.test.ts b/packages/catalog-process/test/createRiskAnalysis.test.ts index 595fde0328..f68c947c36 100644 --- a/packages/catalog-process/test/createRiskAnalysis.test.ts +++ b/packages/catalog-process/test/createRiskAnalysis.test.ts @@ -10,6 +10,7 @@ import { getMockTenant, getMockValidRiskAnalysis, decodeProtobufPayload, + getMockDelegation, } from "pagopa-interop-commons-test/index.js"; import { TenantKind, @@ -22,7 +23,9 @@ import { toEServiceV2, unsafeBrandId, operationForbidden, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { catalogApi } from "pagopa-interop-api-clients"; import { expect, describe, it } from "vitest"; @@ -44,6 +47,7 @@ import { readLastEserviceEvent, getMockDescriptor, getMockEService, + addOneDelegation, } from "./utils.js"; describe("create risk analysis", () => { @@ -141,6 +145,104 @@ describe("create risk analysis", () => { ); expect(writtenPayload.eservice).toEqual(expectedEservice); }); + it("should write on event-store for the creation of a risk analysis (delegate)", async () => { + const producerTenantKind: TenantKind = randomArrayItem( + Object.values(tenantKind) + ); + const producer: Tenant = { + ...getMockTenant(), + kind: producerTenantKind, + }; + + const mockValidRiskAnalysis = getMockValidRiskAnalysis(producerTenantKind); + const riskAnalysisSeed: catalogApi.EServiceRiskAnalysisSeed = + buildRiskAnalysisSeed(mockValidRiskAnalysis); + + const eservice: EService = { + ...mockEService, + producerId: producer.id, + mode: eserviceMode.receive, + descriptors: [ + { + ...mockDescriptor, + state: descriptorState.draft, + }, + ], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneTenant(producer); + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.createRiskAnalysis(eservice.id, riskAnalysisSeed, { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceRiskAnalysisAdded"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceRiskAnalysisAddedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + riskAnalysis: [ + { + ...mockValidRiskAnalysis, + id: unsafeBrandId(writtenPayload.eservice!.riskAnalysis[0]!.id), + createdAt: new Date( + Number(writtenPayload.eservice!.riskAnalysis[0]!.createdAt) + ), + riskAnalysisForm: { + ...mockValidRiskAnalysis.riskAnalysisForm, + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.id + ), + singleAnswers: + mockValidRiskAnalysis.riskAnalysisForm.singleAnswers.map( + (singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + }) + ), + multiAnswers: + mockValidRiskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + }, + }, + ], + }); + + expect(writtenPayload.riskAnalysisId).toEqual( + expectedEservice.riskAnalysis[0].id + ); + expect(writtenPayload.eservice).toEqual(expectedEservice); + }); it("should throw eServiceNotFound if the eservice doesn't exist", async () => { expect( catalogService.createRiskAnalysis( @@ -170,6 +272,28 @@ describe("create risk analysis", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + expect( + catalogService.createRiskAnalysis( + mockEService.id, + buildRiskAnalysisSeed(getMockValidRiskAnalysis(tenantKind.PA)), + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("should throw eserviceNotInDraftState if the eservice is not in draft state", async () => { const eservice: EService = { ...mockEService, diff --git a/packages/catalog-process/test/deleteDocument.test.ts b/packages/catalog-process/test/deleteDocument.test.ts index 3520df9f95..cf8c198c3c 100644 --- a/packages/catalog-process/test/deleteDocument.test.ts +++ b/packages/catalog-process/test/deleteDocument.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger, fileManagerDeleteError } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -9,13 +12,15 @@ import { toEServiceV2, EServiceDescriptorInterfaceDeletedV2, operationForbidden, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { vi, expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, eServiceDocumentNotFound, } from "../src/model/domain/errors.js"; import { config } from "../src/config/config.js"; @@ -28,6 +33,7 @@ import { getMockDescriptor, getMockEService, getMockDocument, + addOneDelegation, } from "./utils.js"; describe("delete Document", () => { @@ -36,7 +42,9 @@ describe("delete Document", () => { const mockDocument = getMockDocument(); it.each( Object.values(descriptorState).filter( - (state) => state !== descriptorState.archived + (state) => + state !== descriptorState.archived && + state !== descriptorState.waitingForApproval ) )( "should write on event-store for the deletion of a document, and delete the file from the bucket, for %s descriptor", @@ -197,6 +205,92 @@ describe("delete Document", () => { ).not.toContain(interfaceDocument.path); }); + it("should write on event-store for the deletion of a document that is the descriptor interface, and delete the file from the bucket (delegate)", async () => { + vi.spyOn(fileManager, "delete"); + + const interfaceDocument = { + ...mockDocument, + path: `${config.eserviceDocumentsPath}/${mockDocument.id}/${mockDocument.name}`, + }; + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + interface: interfaceDocument, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + await fileManager.storeBytes( + { + bucket: config.s3Bucket, + path: config.eserviceDocumentsPath, + resourceId: interfaceDocument.id, + name: interfaceDocument.name, + content: Buffer.from("testtest"), + }, + genericLogger + ); + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(interfaceDocument.path); + + await catalogService.deleteDocument( + eservice.id, + descriptor.id, + interfaceDocument.id, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceDescriptorInterfaceDeleted"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorInterfaceDeletedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + interface: undefined, + serverUrls: [], + }, + ], + }); + + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.documentId).toEqual(interfaceDocument.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + + expect(fileManager.delete).toHaveBeenCalledWith( + config.s3Bucket, + interfaceDocument.path, + genericLogger + ); + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).not.toContain(interfaceDocument.path); + }); + it("should fail if the file deletion fails", async () => { config.s3Bucket = "invalid-bucket"; // configure an invalid bucket to force a failure @@ -271,6 +365,38 @@ describe("delete Document", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + docs: [mockDocument], + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + expect( + catalogService.deleteDocument( + eservice.id, + descriptor.id, + mockDocument.id, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { const eservice: EService = { ...mockEService, @@ -294,12 +420,8 @@ describe("delete Document", () => { eServiceDescriptorNotFound(eservice.id, mockDescriptor.id) ); }); - it.each( - Object.values(descriptorState).filter( - (state) => state === descriptorState.archived - ) - )( - "should throw notValidDescriptor when trying to delete a document with descriptor in %s state", + it.each([descriptorState.archived])( + "should throw notValidDescriptorState when trying to delete a document with descriptor in %s state", async (state) => { const descriptor: Descriptor = { ...getMockDescriptor(state), @@ -322,7 +444,7 @@ describe("delete Document", () => { logger: genericLogger, } ) - ).rejects.toThrowError(notValidDescriptor(descriptor.id, state)); + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); } ); it.each( @@ -330,7 +452,7 @@ describe("delete Document", () => { (state) => state !== descriptorState.draft ) )( - "should throw notValidDescriptor when trying to delete an interface with descriptor in %s state", + "should throw notValidDescriptorState when trying to delete an interface with descriptor in %s state", async (state) => { const descriptor: Descriptor = { ...getMockDescriptor(state), @@ -353,7 +475,7 @@ describe("delete Document", () => { logger: genericLogger, } ) - ).rejects.toThrowError(notValidDescriptor(descriptor.id, state)); + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); } ); it("should throw eServiceDocumentNotFound if the document doesn't exist", async () => { diff --git a/packages/catalog-process/test/deleteDraftDescriptor.test.ts b/packages/catalog-process/test/deleteDraftDescriptor.test.ts index 886c3b146d..2b56e526de 100644 --- a/packages/catalog-process/test/deleteDraftDescriptor.test.ts +++ b/packages/catalog-process/test/deleteDraftDescriptor.test.ts @@ -2,6 +2,7 @@ import { genericLogger, fileManagerDeleteError } from "pagopa-interop-commons"; import { decodeProtobufPayload, + getMockDelegation, readEventByStreamIdAndVersion, } from "pagopa-interop-commons-test/index.js"; import { @@ -14,13 +15,15 @@ import { DescriptorId, generateId, EServiceDeletedV2, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { vi, expect, describe, it } from "vitest"; import { config } from "../src/config/config.js"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, } from "../src/model/domain/errors.js"; import { fileManager, @@ -32,6 +35,7 @@ import { getMockDescriptor, getMockDocument, postgresDB, + addOneDelegation, } from "./utils.js"; describe("delete draft descriptor", () => { @@ -293,6 +297,81 @@ describe("delete draft descriptor", () => { }); }); + it("should write on event-store for the deletion of a draft descriptor and the entire eservice (delegate)", async () => { + const draftDescriptor: Descriptor = { + ...getMockDescriptor(descriptorState.draft), + }; + + const eservice: EService = { + ...getMockEService(), + descriptors: [draftDescriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.deleteDraftDescriptor( + eservice.id, + draftDescriptor.id, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const descriptorDeletionEvent = await readEventByStreamIdAndVersion( + eservice.id, + 1, + "catalog", + postgresDB + ); + + const eserviceDeletionEvent = await readLastEserviceEvent(eservice.id); + + expect(descriptorDeletionEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDraftDescriptorDeleted", + event_version: 2, + }); + expect(eserviceDeletionEvent).toMatchObject({ + stream_id: eservice.id, + version: "2", + type: "EServiceDeleted", + event_version: 2, + }); + + const descriptorDeletionPayload = decodeProtobufPayload({ + messageType: EServiceDraftDescriptorDeletedV2, + payload: descriptorDeletionEvent.data, + }); + const eserviceDeletionPayload = decodeProtobufPayload({ + messageType: EServiceDeletedV2, + payload: eserviceDeletionEvent.data, + }); + + const expectedEserviceBeforeDeletion: EService = { + ...eservice, + descriptors: [], + }; + + expect(descriptorDeletionPayload).toEqual({ + eservice: toEServiceV2(expectedEserviceBeforeDeletion), + descriptorId: draftDescriptor.id, + }); + expect(eserviceDeletionPayload).toEqual({ + eserviceId: eservice.id, + eservice: toEServiceV2(expectedEserviceBeforeDeletion), + }); + }); + it("should fail if one of the file deletions fails", async () => { config.s3Bucket = "invalid-bucket"; // configure an invalid bucket to force a failure @@ -383,8 +462,39 @@ describe("delete draft descriptor", () => { ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const publishedDescriptor: Descriptor = { + ...getMockDescriptor(descriptorState.published), + version: "1", + }; + const descriptorToDelete: Descriptor = { + ...getMockDescriptor(descriptorState.draft), + version: "2", + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [publishedDescriptor, descriptorToDelete], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + expect( + catalogService.deleteDraftDescriptor(eservice.id, descriptorToDelete.id, { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); + it.each([descriptorState.published, descriptorState.suspended])( - "should throw notValidDescriptor if the eservice is in %s state", + "should throw notValidDescriptorState if the eservice is in %s state", async (state) => { const descriptor: Descriptor = { ...getMockDescriptor(state), @@ -403,12 +513,12 @@ describe("delete draft descriptor", () => { serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError(notValidDescriptor(descriptor.id, state)); + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); } ); it.each([descriptorState.deprecated, descriptorState.archived])( - "should throw notValidDescriptor if the eservice is in %s state", + "should throw notValidDescriptorState if the eservice is in %s state", async (state) => { const descriptorToDelete: Descriptor = { ...getMockDescriptor(state), @@ -436,7 +546,9 @@ describe("delete draft descriptor", () => { logger: genericLogger, } ) - ).rejects.toThrowError(notValidDescriptor(descriptorToDelete.id, state)); + ).rejects.toThrowError( + notValidDescriptorState(descriptorToDelete.id, state) + ); } ); }); diff --git a/packages/catalog-process/test/deleteEservice.test.ts b/packages/catalog-process/test/deleteEservice.test.ts index 48d7925a7d..ea5f24853c 100644 --- a/packages/catalog-process/test/deleteEservice.test.ts +++ b/packages/catalog-process/test/deleteEservice.test.ts @@ -2,6 +2,7 @@ import { genericLogger } from "pagopa-interop-commons"; import { decodeProtobufPayload, + getMockDelegation, readEventByStreamIdAndVersion, } from "pagopa-interop-commons-test/index.js"; import { @@ -12,12 +13,15 @@ import { operationForbidden, EServiceDraftDescriptorDeletedV2, toEServiceV2, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it, vi } from "vitest"; import { eServiceNotFound, eserviceNotInDraftState, + eserviceWithActiveOrPendingDelegation, } from "../src/model/domain/errors.js"; import { config } from "../src/config/config.js"; import { @@ -30,6 +34,7 @@ import { getMockEService, postgresDB, fileManager, + addOneDelegation, } from "./utils.js"; describe("delete eservice", () => { @@ -207,6 +212,54 @@ describe("delete eservice", () => { ).rejects.toThrowError(operationForbidden); }); + it.each([delegationState.active, delegationState.waitingForApproval])( + "should throw eserviceWithActiveOrPendingDelegation if the eservice is associated with a delegation with state %s", + async (delegationState) => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + expect( + catalogService.deleteEService(mockEService.id, { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError( + eserviceWithActiveOrPendingDelegation(mockEService.id, delegation.id) + ); + } + ); + + it.each([delegationState.revoked, delegationState.rejected])( + "should not throw eserviceWithActiveOrPendingDelegation if the eservice is associated with a delegation with state %s", + async (delegationState) => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + expect( + catalogService.deleteEService(mockEService.id, { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).resolves.not.toThrowError( + eserviceWithActiveOrPendingDelegation(mockEService.id, delegation.id) + ); + } + ); + it("should throw eserviceNotInDraftState if the eservice has both draft and non-draft descriptors", async () => { const descriptor1: Descriptor = { ...mockDescriptor, diff --git a/packages/catalog-process/test/deleteRiskAnalysis.test.ts b/packages/catalog-process/test/deleteRiskAnalysis.test.ts index 5c47a29a81..7df1ab5eb7 100644 --- a/packages/catalog-process/test/deleteRiskAnalysis.test.ts +++ b/packages/catalog-process/test/deleteRiskAnalysis.test.ts @@ -3,6 +3,7 @@ import { genericLogger } from "pagopa-interop-commons"; import { getMockValidRiskAnalysis, decodeProtobufPayload, + getMockDelegation, } from "pagopa-interop-commons-test/index.js"; import { EService, @@ -13,6 +14,8 @@ import { Descriptor, descriptorState, operationForbidden, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { @@ -21,6 +24,7 @@ import { eserviceNotInDraftState, } from "../src/model/domain/errors.js"; import { + addOneDelegation, addOneEService, catalogService, getMockAuthData, @@ -74,6 +78,93 @@ describe("delete risk analysis", () => { eservice: expectedEservice, }); }); + it("should write on event-store for the deletion of a risk analysis (delegate)", async () => { + const riskAnalysis = getMockValidRiskAnalysis("PA"); + const eservice: EService = { + ...mockEService, + descriptors: [], + riskAnalysis: [riskAnalysis], + mode: "Receive", + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.deleteRiskAnalysis(eservice.id, riskAnalysis.id, { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + const expectedEservice = toEServiceV2({ + ...eservice, + riskAnalysis: eservice.riskAnalysis.filter( + (r) => r.id !== riskAnalysis.id + ), + }); + + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceRiskAnalysisDeleted", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceRiskAnalysisDeletedV2, + payload: writtenEvent.data, + }); + expect(writtenPayload).toEqual({ + riskAnalysisId: riskAnalysis.id, + eservice: expectedEservice, + }); + }); + it("should write on event-store for the deletion of a risk analysis", async () => { + const riskAnalysis = getMockValidRiskAnalysis("PA"); + const eservice: EService = { + ...mockEService, + descriptors: [], + riskAnalysis: [riskAnalysis], + mode: "Receive", + }; + await addOneEService(eservice); + + await catalogService.deleteRiskAnalysis(eservice.id, riskAnalysis.id, { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + const expectedEservice = toEServiceV2({ + ...eservice, + riskAnalysis: eservice.riskAnalysis.filter( + (r) => r.id !== riskAnalysis.id + ), + }); + + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceRiskAnalysisDeleted", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceRiskAnalysisDeletedV2, + payload: writtenEvent.data, + }); + expect(writtenPayload).toEqual({ + riskAnalysisId: riskAnalysis.id, + eservice: expectedEservice, + }); + }); it("should throw eServiceNotFound if the eservice doesn't exist", () => { expect( catalogService.deleteRiskAnalysis( @@ -166,4 +257,39 @@ describe("delete risk analysis", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.published, + interface: mockDocument, + publishedAt: new Date(), + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + riskAnalysis: [getMockValidRiskAnalysis("PA")], + mode: "Receive", + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.deleteRiskAnalysis( + eservice.id, + generateId(), + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); }); diff --git a/packages/catalog-process/test/getDocumentById.test.ts b/packages/catalog-process/test/getDocumentById.test.ts index a7acd6c4fd..3a33d84f90 100644 --- a/packages/catalog-process/test/getDocumentById.test.ts +++ b/packages/catalog-process/test/getDocumentById.test.ts @@ -5,14 +5,18 @@ import { EService, generateId, descriptorState, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; +import { getMockDelegation } from "pagopa-interop-commons-test/index.js"; import { eServiceNotFound, eServiceDescriptorNotFound, eServiceDocumentNotFound, } from "../src/model/domain/errors.js"; import { + addOneDelegation, addOneEService, catalogService, getMockAuthData, @@ -90,6 +94,49 @@ describe("get document by id", () => { expect(result).toEqual(mockDocument); }); + it("should get the interface if it exists (requester is the delegate, admin)", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + interface: mockDocument, + docs: [], + }; + const eservice: EService = { + ...mockEService, + id: generateId(), + name: "eservice 001", + descriptors: [descriptor], + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + const authData: AuthData = { + ...getMockAuthData(delegation.delegateId), + userRoles: [userRoles.ADMIN_ROLE], + }; + + await addOneEService(eservice); + await addOneDelegation(delegation); + + const result = await catalogService.getDocumentById( + { + eserviceId: eservice.id, + descriptorId: descriptor.id, + documentId: mockDocument.id, + }, + { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "", + } + ); + expect(result).toEqual(mockDocument); + }); + it("should throw eServiceNotFound if the eservice doesn't exist", async () => { const authData: AuthData = { ...getMockAuthData(), diff --git a/packages/catalog-process/test/getEserviceById.test.ts b/packages/catalog-process/test/getEserviceById.test.ts index af88cf1782..bff531bbc6 100644 --- a/packages/catalog-process/test/getEserviceById.test.ts +++ b/packages/catalog-process/test/getEserviceById.test.ts @@ -6,10 +6,14 @@ import { EService, generateId, EServiceId, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; +import { getMockDelegation } from "pagopa-interop-commons-test/index.js"; import { eServiceNotFound } from "../src/model/domain/errors.js"; import { + addOneDelegation, addOneEService, catalogService, getMockAuthData, @@ -88,14 +92,58 @@ describe("get eservice by id", () => { ).rejects.toThrowError(eServiceNotFound(notExistingId)); }); - it("should throw eServiceNotFound if there is only a draft descriptor (requester is not the producer)", async () => { - const descriptor = { - ...mockDescriptor, - state: descriptorState.draft, - }; + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should throw eServiceNotFound if there is only a %s descriptor (requester is not the producer)", + async (state) => { + const descriptor = { + ...mockDescriptor, + state, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(mockEService); + expect( + catalogService.getEServiceById(eservice.id, { + authData: getMockAuthData(), + logger: genericLogger, + correlationId: generateId(), + serviceName: "", + }) + ).rejects.toThrowError(eServiceNotFound(eservice.id)); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should throw eServiceNotFound if there is only a %s descriptor (requester is the producer but not admin nor api, nor support)", + async (state) => { + const descriptor = { + ...mockDescriptor, + state, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const authData: AuthData = { + ...getMockAuthData(), + userRoles: [userRoles.SECURITY_ROLE], + }; + await addOneEService(mockEService); + expect( + catalogService.getEServiceById(eservice.id, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "", + }) + ).rejects.toThrowError(eServiceNotFound(eservice.id)); + } + ); + it("should throw eServiceNotFound if there are no descriptors (requester is not the producer)", async () => { const eservice: EService = { ...mockEService, - descriptors: [descriptor], + descriptors: [], }; await addOneEService(mockEService); expect( @@ -107,7 +155,7 @@ describe("get eservice by id", () => { }) ).rejects.toThrowError(eServiceNotFound(eservice.id)); }); - it("should throw eServiceNotFound if there is only a draft descriptor (requester is the producer but not admin nor api, nor support)", async () => { + it("should throw eServiceNotFound if there are no descriptors (requester is the producer but not admin, nor api, nor support)", async () => { const descriptor = { ...mockDescriptor, state: descriptorState.draft, @@ -117,7 +165,7 @@ describe("get eservice by id", () => { descriptors: [descriptor], }; const authData: AuthData = { - ...getMockAuthData(), + ...getMockAuthData(eservice.producerId), userRoles: [userRoles.SECURITY_ROLE], }; await addOneEService(mockEService); @@ -130,44 +178,107 @@ describe("get eservice by id", () => { }) ).rejects.toThrowError(eServiceNotFound(eservice.id)); }); - it("should throw eServiceNotFound if there are no descriptors (requester is not the producer)", async () => { - const eservice: EService = { - ...mockEService, - descriptors: [], - }; - await addOneEService(mockEService); - expect( - catalogService.getEServiceById(eservice.id, { - authData: getMockAuthData(), + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should filter out the %s descriptors if the eservice has both of that state and not (requester is not the producer)", + async (state) => { + const descriptorA: Descriptor = { + ...mockDescriptor, + state, + }; + const descriptorB: Descriptor = { + ...mockDescriptor, + state: descriptorState.published, + interface: mockDocument, + publishedAt: new Date(), + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptorA, descriptorB], + }; + const authData: AuthData = { + ...getMockAuthData(), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice); + const result = await catalogService.getEServiceById(eservice.id, { + authData, logger: genericLogger, correlationId: generateId(), serviceName: "", - }) - ).rejects.toThrowError(eServiceNotFound(eservice.id)); - }); - it("should throw eServiceNotFound if there are no descriptors (requester is the producer but not admin, nor api, nor support)", async () => { - const descriptor = { - ...mockDescriptor, - state: descriptorState.draft, - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - const authData: AuthData = { - ...getMockAuthData(eservice.producerId), - userRoles: [userRoles.SECURITY_ROLE], - }; - await addOneEService(mockEService); - expect( - catalogService.getEServiceById(eservice.id, { + }); + expect(result.descriptors).toEqual([descriptorB]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should filter out the %s descriptors if the eservice has both of that state and not (requester is the producer but not admin nor api, nor support)", + async (state) => { + const descriptorA: Descriptor = { + ...mockDescriptor, + state, + }; + const descriptorB: Descriptor = { + ...mockDescriptor, + state: descriptorState.published, + interface: mockDocument, + publishedAt: new Date(), + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptorA, descriptorB], + }; + const authData: AuthData = { + ...getMockAuthData(eservice.producerId), + userRoles: [userRoles.SECURITY_ROLE], + }; + await addOneEService(eservice); + const result = await catalogService.getEServiceById(eservice.id, { authData, logger: genericLogger, correlationId: generateId(), serviceName: "", - }) - ).rejects.toThrowError(eServiceNotFound(eservice.id)); - }); + }); + expect(result.descriptors).toEqual([descriptorB]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should not filter out the %s descriptors if the eservice has both of that state and not (requester is delegate)", + async (state) => { + const descriptorA: Descriptor = { + ...mockDescriptor, + state, + }; + const descriptorB: Descriptor = { + ...mockDescriptor, + state: descriptorState.published, + interface: mockDocument, + publishedAt: new Date(), + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptorA, descriptorB], + }; + const authData: AuthData = { + ...getMockAuthData(), + userRoles: [userRoles.ADMIN_ROLE], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: authData.organizationId, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + const result = await catalogService.getEServiceById(eservice.id, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "", + }); + expect(result.descriptors).toEqual([descriptorA, descriptorB]); + } + ); it("should filter out the draft descriptors if the eservice has both draft and non-draft ones (requester is not the producer)", async () => { const descriptorA: Descriptor = { ...mockDescriptor, @@ -224,4 +335,39 @@ describe("get eservice by id", () => { }); expect(result.descriptors).toEqual([descriptorB]); }); + it("should not filter out the draft descriptors if the eservice has both draft and non-draft ones (requester is delegate)", async () => { + const descriptorA: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const descriptorB: Descriptor = { + ...mockDescriptor, + state: descriptorState.published, + interface: mockDocument, + publishedAt: new Date(), + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptorA, descriptorB], + }; + const authData: AuthData = { + ...getMockAuthData(), + userRoles: [userRoles.ADMIN_ROLE], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: authData.organizationId, + eserviceId: eservice.id, + state: delegationState.active, + }); + await addOneEService(eservice); + await addOneDelegation(delegation); + const result = await catalogService.getEServiceById(eservice.id, { + authData, + logger: genericLogger, + correlationId: generateId(), + serviceName: "", + }); + expect(result.descriptors).toEqual([descriptorA, descriptorB]); + }); }); diff --git a/packages/catalog-process/test/getEservices.test.ts b/packages/catalog-process/test/getEservices.test.ts index 49dacf79ac..b431865a42 100644 --- a/packages/catalog-process/test/getEservices.test.ts +++ b/packages/catalog-process/test/getEservices.test.ts @@ -10,8 +10,11 @@ import { eserviceMode, Tenant, agreementState, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { beforeEach, expect, describe, it } from "vitest"; +import { getMockDelegation } from "pagopa-interop-commons-test"; import { addOneEService, addOneTenant, @@ -23,6 +26,7 @@ import { getMockDocument, getMockAgreement, getMockEServiceAttributes, + addOneDelegation, } from "./utils.js"; describe("get eservices", () => { @@ -206,6 +210,62 @@ describe("get eservices", () => { expect(result.totalCount).toBe(3); expect(result.results).toEqual([eservice1, eservice2, eservice3]); }); + it("should get the eServices, including the ones with an active delegation, if they exist (parameters: producersIds)", async () => { + const delegatedOrganization1 = generateId(); + const delegatedOrganization2 = generateId(); + + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: delegatedOrganization1, + eserviceId: eservice4.id, + state: delegationState.active, + }); + await addOneDelegation(delegation1); + + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice5.id, + state: delegationState.active, + delegateId: delegatedOrganization2, + }); + + await addOneDelegation(delegation2); + + const delegation3 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice6.id, + state: delegationState.rejected, + delegateId: delegatedOrganization2, + }); + + await addOneDelegation(delegation3); + + const result = await catalogService.getEServices( + getMockAuthData(), + { + eservicesIds: [], + producersIds: [ + organizationId1, + delegatedOrganization1, + delegatedOrganization2, + ], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(5); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + ]); + }); it("should get the eServices if they exist (parameters: states)", async () => { const result = await catalogService.getEServices( getMockAuthData(), @@ -286,6 +346,114 @@ describe("get eservices", () => { eservice5, ]); }); + it("should get the eServices if they exist (parameters: delegated = true)", async () => { + const delegatedOrganization1 = generateId(); + const delegatedOrganization2 = generateId(); + + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice4.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + await addOneDelegation(delegation1); + + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice5.id, + delegateId: delegatedOrganization2, + state: delegationState.waitingForApproval, + }); + + await addOneDelegation(delegation2); + + const delegation3 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice6.id, + delegateId: delegatedOrganization2, + state: delegationState.rejected, + }); + + await addOneDelegation(delegation3); + + const result = await catalogService.getEServices( + getMockAuthData(), + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + delegated: true, + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(2); + expect(result.results).toEqual([eservice4, eservice5]); + }); + it("should get the eServices if they exist (parameters: delegated = false)", async () => { + const delegatedOrganization1 = generateId(); + const delegatedOrganization2 = generateId(); + + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice4.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + await addOneDelegation(delegation1); + + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice5.id, + delegateId: delegatedOrganization2, + state: delegationState.waitingForApproval, + }); + + await addOneDelegation(delegation2); + + const delegation3 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice6.id, + delegateId: delegatedOrganization2, + state: delegationState.rejected, + }); + + await addOneDelegation(delegation3); + + const delegation4 = getMockDelegation({ + kind: delegationKind.delegatedConsumer, + eserviceId: eservice6.id, + delegateId: delegatedOrganization2, + state: delegationState.active, + }); + + await addOneDelegation(delegation4); + + const result = await catalogService.getEServices( + getMockAuthData(), + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + delegated: false, + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(4); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice6, + ]); + }); it("should get the eServices if they exist (parameters: statestates, name)", async () => { const result = await catalogService.getEServices( getMockAuthData(organizationId3), @@ -340,6 +508,157 @@ describe("get eservices", () => { expect(result.totalCount).toBe(1); expect(result.results).toEqual([eservice5]); }); + it("should get the eServices, including the ones with an active delegation, if they exist (parameters: producersIds, states, name)", async () => { + const delegatedOrganization1: TenantId = generateId(); + const delegatedOrganization2: TenantId = generateId(); + + const delegatedEService1: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 1 test", + producerId: organizationId1, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + state: descriptorState.published, + }, + ], + }; + + const delegatedEService2: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 2", + producerId: organizationId1, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + state: descriptorState.published, + }, + ], + }; + + const delegatedEService3: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 3 test", + producerId: organizationId1, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.draft, + }, + ], + }; + + const delegatedEService4: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 4 test", + producerId: organizationId1, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + }, + ], + }; + + const delegatedEService5: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 5 test", + producerId: organizationId1, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + }, + ], + }; + + await addOneEService(delegatedEService1); + await addOneEService(delegatedEService2); + await addOneEService(delegatedEService3); + await addOneEService(delegatedEService4); + await addOneEService(delegatedEService5); + + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService1.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + await addOneDelegation(delegation1); + + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService2.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + + await addOneDelegation(delegation2); + + const delegation3 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService3.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + + await addOneDelegation(delegation3); + + const delegation4 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService4.id, + delegateId: delegatedOrganization2, + state: delegationState.active, + }); + + await addOneDelegation(delegation4); + + const delegation5 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService5.id, + delegateId: delegatedOrganization2, + state: delegationState.waitingForApproval, + }); + + await addOneDelegation(delegation5); + + const result = await catalogService.getEServices( + getMockAuthData(), + { + eservicesIds: [], + producersIds: [ + organizationId2, + delegatedOrganization1, + delegatedOrganization2, + ], + states: ["Published"], + agreementStates: [], + name: "test", + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(3); + expect(result.results).toEqual([ + delegatedEService1, + delegatedEService4, + eservice5, + ]); + }); it("should not get the eServices if they don't exist (parameters: producersIds, states, name)", async () => { const result = await catalogService.getEServices( getMockAuthData(), @@ -440,7 +759,7 @@ describe("get eservices", () => { }); }); - it("should get the eServices if they exist (parameters: producerIds, mode)", async () => { + it("should get the eServices if they exist (parameters: producersIds, mode)", async () => { const result = await catalogService.getEServices( getMockAuthData(), { @@ -461,6 +780,170 @@ describe("get eservices", () => { }); }); + it("should get the eServices if they exist (parameters: producersIds, mode, delegated = true)", async () => { + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice4.id, + delegateId: organizationId3, + state: delegationState.active, + }); + await addOneDelegation(delegation1); + + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice4.id, + delegateId: organizationId3, + state: delegationState.active, + }); + + await addOneDelegation(delegation2); + + const result = await catalogService.getEServices( + getMockAuthData(), + { + eservicesIds: [], + producersIds: [organizationId2], + states: [], + agreementStates: [], + attributesIds: [], + mode: eserviceMode.deliver, + delegated: true, + }, + 0, + 50, + genericLogger + ); + expect(result).toEqual({ + totalCount: 1, + results: [eservice4], + }); + }); + + it("should get the eServices, including the ones with an active delegation, if they exist (parameters: producersIds, mode)", async () => { + const delegatedOrganization1: TenantId = generateId(); + const delegatedOrganization2: TenantId = generateId(); + + const delegatedEService1: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 1", + producerId: organizationId1, + mode: eserviceMode.deliver, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + }, + ], + }; + + const delegatedEService2: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 2", + producerId: organizationId1, + mode: eserviceMode.receive, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + }, + ], + }; + + const delegatedEService3: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 3", + producerId: organizationId1, + mode: eserviceMode.deliver, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + }, + ], + }; + + const delegatedEService4: EService = { + ...mockEService, + id: generateId(), + name: "delegated eservice 4", + producerId: organizationId1, + mode: eserviceMode.deliver, + descriptors: [ + { + ...mockDescriptor, + id: generateId(), + state: descriptorState.published, + }, + ], + }; + + await addOneEService(delegatedEService1); + await addOneEService(delegatedEService2); + await addOneEService(delegatedEService3); + await addOneEService(delegatedEService4); + + const delegation1 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService1.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + await addOneDelegation(delegation1); + + const delegation2 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService2.id, + delegateId: delegatedOrganization1, + state: delegationState.active, + }); + await addOneDelegation(delegation2); + + const delegation3 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService3.id, + delegateId: delegatedOrganization2, + state: delegationState.active, + }); + await addOneDelegation(delegation3); + + const delegation4 = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: delegatedEService4.id, + delegateId: delegatedOrganization2, + state: delegationState.rejected, + }); + await addOneDelegation(delegation4); + + const result = await catalogService.getEServices( + getMockAuthData(), + { + eservicesIds: [], + producersIds: [ + organizationId2, + delegatedOrganization1, + delegatedOrganization2, + ], + states: [], + agreementStates: [], + attributesIds: [], + mode: eserviceMode.deliver, + }, + 0, + 50, + genericLogger + ); + expect(result).toEqual({ + totalCount: 4, + results: [delegatedEService1, delegatedEService3, eservice4, eservice5], + }); + }); + it("should not get the eServices if they don't exist (parameters: attributesIds)", async () => { const result = await catalogService.getEServices( getMockAuthData(), @@ -686,278 +1169,356 @@ describe("get eservices", () => { eservice6, ]); }); - it("should include eservices whose only descriptor is draft (requester is the producer, admin)", async () => { - const descriptor8: Descriptor = { - ...mockDescriptor, - id: generateId(), - state: descriptorState.draft, - }; - const eservice8: EService = { - ...mockEService, - id: generateId(), - name: "eservice 008", - producerId: organizationId1, - descriptors: [descriptor8], - }; - const authData: AuthData = { - ...getMockAuthData(organizationId1), - userRoles: [userRoles.ADMIN_ROLE], - }; - await addOneEService(eservice8); - const result = await catalogService.getEServices( - authData, - { - eservicesIds: [], - producersIds: [], - states: [], - agreementStates: [], - attributesIds: [], - }, - 0, - 50, - genericLogger - ); - expect(result.totalCount).toBe(7); - expect(result.results).toEqual([ - eservice1, - eservice2, - eservice3, - eservice4, - eservice5, - eservice6, - eservice8, - ]); - }); - it("should not include eservices whose only descriptor is draft (requester is the producer, not admin nor api, nor support)", async () => { - const descriptor8: Descriptor = { - ...mockDescriptor, - id: generateId(), - state: descriptorState.draft, - }; - const eservice8: EService = { - ...mockEService, - id: generateId(), - name: "eservice 008", - producerId: organizationId1, - descriptors: [descriptor8], - }; - const authData: AuthData = { - ...getMockAuthData(organizationId1), - userRoles: [userRoles.SECURITY_ROLE], - }; - await addOneEService(eservice8); - const result = await catalogService.getEServices( - authData, - { - eservicesIds: [], - producersIds: [], - states: [], - agreementStates: [], - attributesIds: [], - }, - 0, - 50, - genericLogger - ); - expect(result.totalCount).toBe(6); - expect(result.results).toEqual([ - eservice1, - eservice2, - eservice3, - eservice4, - eservice5, - eservice6, - ]); - }); - it("should not include eservices whose only descriptor is draft (requester is not the producer)", async () => { - const descriptor8: Descriptor = { - ...mockDescriptor, - id: generateId(), - state: descriptorState.draft, - }; - const eservice8: EService = { - ...mockEService, - id: generateId(), - name: "eservice 008", - producerId: organizationId1, - descriptors: [descriptor8], - }; - const authData: AuthData = { - ...getMockAuthData(), - userRoles: [userRoles.ADMIN_ROLE], - }; - await addOneEService(eservice8); - const result = await catalogService.getEServices( - authData, - { - eservicesIds: [], - producersIds: [], - states: [], - agreementStates: [], - attributesIds: [], - }, - 0, - 50, - genericLogger - ); - expect(result.totalCount).toBe(6); - expect(result.results).toEqual([ - eservice1, - eservice2, - eservice3, - eservice4, - eservice5, - eservice6, - ]); - }); - it("should not filter out draft descriptors if the eservice has both draft and non-draft ones (requester is the producer, admin)", async () => { - const descriptor9a: Descriptor = { - ...mockDescriptor, - id: generateId(), - interface: mockDocument, - publishedAt: new Date(), - state: descriptorState.published, - }; - const descriptor9b: Descriptor = { - ...mockDescriptor, - id: generateId(), - version: "2", - state: descriptorState.draft, - }; - const eservice9: EService = { - ...mockEService, - id: generateId(), - name: "eservice 008", - producerId: organizationId1, - descriptors: [descriptor9a, descriptor9b], - }; - const authData: AuthData = { - ...getMockAuthData(organizationId1), - userRoles: [userRoles.ADMIN_ROLE], - }; - await addOneEService(eservice9); - const result = await catalogService.getEServices( - authData, - { - eservicesIds: [], - producersIds: [], - states: [], - agreementStates: [], - attributesIds: [], - }, - 0, - 50, - genericLogger - ); - expect(result.totalCount).toBe(7); - expect(result.results).toEqual([ - eservice1, - eservice2, - eservice3, - eservice4, - eservice5, - eservice6, - eservice9, - ]); - }); - it("should filter out draft descriptors if the eservice has both draft and non-draft ones (requester is the producer, but not admin nor api, nor support)", async () => { - const descriptor9a: Descriptor = { - ...mockDescriptor, - id: generateId(), - interface: mockDocument, - publishedAt: new Date(), - state: descriptorState.published, - }; - const descriptor9b: Descriptor = { - ...mockDescriptor, - id: generateId(), - version: "2", - state: descriptorState.draft, - }; - const eservice9: EService = { - ...mockEService, - id: generateId(), - name: "eservice 008", - producerId: organizationId1, - descriptors: [descriptor9a, descriptor9b], - }; - const authData: AuthData = { - ...getMockAuthData(organizationId1), - userRoles: [userRoles.SECURITY_ROLE], - }; - await addOneEService(eservice9); - const result = await catalogService.getEServices( - authData, - { - eservicesIds: [], - producersIds: [], - states: [], - agreementStates: [], - attributesIds: [], - }, - 0, - 50, - genericLogger - ); - expect(result.totalCount).toBe(7); - expect(result.results).toEqual([ - eservice1, - eservice2, - eservice3, - eservice4, - eservice5, - eservice6, - { ...eservice9, descriptors: [descriptor9a] }, - ]); - }); - it("should filter out draft descriptors if the eservice has both draft and non-draft ones (requester is not the producer)", async () => { - const descriptor9a: Descriptor = { - ...mockDescriptor, - id: generateId(), - interface: mockDocument, - publishedAt: new Date(), - state: descriptorState.published, - }; - const descriptor9b: Descriptor = { - ...mockDescriptor, - id: generateId(), - version: "2", - state: descriptorState.draft, - }; - const eservice9: EService = { - ...mockEService, - id: generateId(), - name: "eservice 008", - producerId: organizationId1, - descriptors: [descriptor9a, descriptor9b], - }; - const authData: AuthData = { - ...getMockAuthData(), - userRoles: [userRoles.ADMIN_ROLE], - }; - await addOneEService(eservice9); - const result = await catalogService.getEServices( - authData, - { - eservicesIds: [], - producersIds: [], - states: [], - agreementStates: [], - attributesIds: [], - }, - 0, - 50, - genericLogger - ); - expect(result.totalCount).toBe(7); - expect(result.results).toEqual([ - eservice1, - eservice2, - eservice3, - eservice4, - eservice5, - eservice6, - { ...eservice9, descriptors: [descriptor9a] }, - ]); - }); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should include eservices whose only descriptor is %s (requester is the producer, admin)", + async (state) => { + const descriptor8: Descriptor = { + ...mockDescriptor, + id: generateId(), + state, + }; + const eservice8: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor8], + }; + const authData: AuthData = { + ...getMockAuthData(organizationId1), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice8); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + eservice8, + ]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should not include eservices whose only descriptor is %s (requester is the producer, not admin nor api, nor support)", + async (state) => { + const descriptor8: Descriptor = { + ...mockDescriptor, + id: generateId(), + state, + }; + const eservice8: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor8], + }; + const authData: AuthData = { + ...getMockAuthData(organizationId1), + userRoles: [userRoles.SECURITY_ROLE], + }; + await addOneEService(eservice8); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(6); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + ]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should not include eservices whose only descriptor is %s (requester is not the producer)", + async (state) => { + const descriptor8: Descriptor = { + ...mockDescriptor, + id: generateId(), + state, + }; + const eservice8: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor8], + }; + const authData: AuthData = { + ...getMockAuthData(), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice8); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(6); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + ]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should not filter out %s descriptors if the eservice has both of that state and not (requester is the producer, admin)", + async (state) => { + const descriptor9a: Descriptor = { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + publishedAt: new Date(), + state: descriptorState.published, + }; + const descriptor9b: Descriptor = { + ...mockDescriptor, + id: generateId(), + version: "2", + state, + }; + const eservice9: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor9a, descriptor9b], + }; + const authData: AuthData = { + ...getMockAuthData(organizationId1), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice9); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + eservice9, + ]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should filter out %s descriptors if the eservice has both of that state and not (requester is the producer, but not admin nor api, nor support)", + async (state) => { + const descriptor9a: Descriptor = { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + publishedAt: new Date(), + state: descriptorState.published, + }; + const descriptor9b: Descriptor = { + ...mockDescriptor, + id: generateId(), + version: "2", + state, + }; + const eservice9: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor9a, descriptor9b], + }; + const authData: AuthData = { + ...getMockAuthData(organizationId1), + userRoles: [userRoles.SECURITY_ROLE], + }; + await addOneEService(eservice9); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + { ...eservice9, descriptors: [descriptor9a] }, + ]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should filter out %s descriptors if the eservice has both of that state and not (requester is not the producer)", + async (state) => { + const descriptor9a: Descriptor = { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + publishedAt: new Date(), + state: descriptorState.published, + }; + const descriptor9b: Descriptor = { + ...mockDescriptor, + id: generateId(), + version: "2", + state, + }; + const eservice9: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor9a, descriptor9b], + }; + const authData: AuthData = { + ...getMockAuthData(), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice9); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + { ...eservice9, descriptors: [descriptor9a] }, + ]); + } + ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should not filter out %s descriptors if the eservice has both of that state and not (requester is delegate, admin)", + async (state) => { + const descriptor9a: Descriptor = { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + publishedAt: new Date(), + state: descriptorState.published, + }; + const descriptor9b: Descriptor = { + ...mockDescriptor, + id: generateId(), + version: "2", + state, + }; + const eservice9: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor9a, descriptor9b], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: organizationId2, + eserviceId: eservice9.id, + state: delegationState.active, + }); + const authData: AuthData = { + ...getMockAuthData(organizationId2), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice9); + await addOneDelegation(delegation); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + eservice9, + ]); + } + ); }); diff --git a/packages/catalog-process/test/publishDescriptor.test.ts b/packages/catalog-process/test/publishDescriptor.test.ts index ce807606dc..729b9a1e07 100644 --- a/packages/catalog-process/test/publishDescriptor.test.ts +++ b/packages/catalog-process/test/publishDescriptor.test.ts @@ -5,6 +5,7 @@ import { randomArrayItem, getMockTenant, getMockValidRiskAnalysis, + getMockDelegation, } from "pagopa-interop-commons-test/index.js"; import { Descriptor, @@ -18,12 +19,15 @@ import { Tenant, generateId, operationForbidden, + delegationState, + EServiceDescriptorDelegateSubmittedV2, + delegationKind, } from "pagopa-interop-models"; import { beforeAll, vi, afterAll, expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, eServiceDescriptorWithoutInterface, tenantNotFound, tenantKindNotFound, @@ -42,6 +46,7 @@ import { getMockDescriptor, getMockDocument, getMockAgreement, + addOneDelegation, } from "./utils.js"; describe("publish descriptor", () => { @@ -163,6 +168,80 @@ describe("publish descriptor", () => { expect(writtenPayload.eservice).toEqual(expectedEservice); }); + it("should write on event-store for the submission of the descriptor by the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + interface: mockDocument, + }; + + const producerTenantKind: TenantKind = randomArrayItem( + Object.values(tenantKind) + ); + const producer: Tenant = { + ...getMockTenant(), + kind: producerTenantKind, + }; + + const riskAnalysis = getMockValidRiskAnalysis(producerTenantKind); + + const eservice: EService = { + ...mockEService, + producerId: producer.id, + mode: eserviceMode.receive, + descriptors: [descriptor], + riskAnalysis: [riskAnalysis], + }; + + const delegate = { + ...getMockTenant(), + kind: producerTenantKind, + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + delegateId: delegate.id, + state: delegationState.active, + }); + + await addOneTenant(producer); + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.publishDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(delegate.id), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorDelegateSubmitted", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDelegateSubmittedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + state: descriptorState.waitingForApproval, + }, + ], + }); + + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + }); + it("should also archive the previously published descriptor", async () => { const descriptor1: Descriptor = { ...mockDescriptor, @@ -202,12 +281,12 @@ describe("publish descriptor", () => { payload: writtenEvent.data, }); - const updatedDescriptor1: Descriptor = { + const expectedDescriptor1: Descriptor = { ...descriptor1, archivedAt: new Date(), state: descriptorState.archived, }; - const updatedDescriptor2: Descriptor = { + const expectedDescriptor2: Descriptor = { ...descriptor2, publishedAt: new Date(), state: descriptorState.published, @@ -215,7 +294,7 @@ describe("publish descriptor", () => { const expectedEservice: EService = { ...eservice, - descriptors: [updatedDescriptor1, updatedDescriptor2], + descriptors: [expectedDescriptor1, expectedDescriptor2], }; expect(writtenPayload).toEqual({ eservice: toEServiceV2(expectedEservice), @@ -273,12 +352,12 @@ describe("publish descriptor", () => { payload: writtenEvent.data, }); - const updatedDescriptor1: Descriptor = { + const expectedDescriptor1: Descriptor = { ...descriptor1, deprecatedAt: new Date(), state: descriptorState.deprecated, }; - const updatedDescriptor2: Descriptor = { + const expectedDescriptor2: Descriptor = { ...descriptor2, publishedAt: new Date(), state: descriptorState.published, @@ -286,7 +365,7 @@ describe("publish descriptor", () => { const expectedEservice: EService = { ...eservice, - descriptors: [updatedDescriptor1, updatedDescriptor2], + descriptors: [expectedDescriptor1, expectedDescriptor2], }; expect(writtenPayload).toEqual({ eservice: toEServiceV2(expectedEservice), @@ -343,7 +422,35 @@ describe("publish descriptor", () => { ).rejects.toThrowError(operationForbidden); }); - it("should throw notValidDescriptor if the descriptor is in published state", async () => { + it("should throw operationForbidden if the requester of the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.publishDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError(operationForbidden); + }); + + it("should throw notValidDescriptorState if the descriptor is in published state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -362,11 +469,11 @@ describe("publish descriptor", () => { logger: genericLogger, }) ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.published) + notValidDescriptorState(descriptor.id, descriptorState.published) ); }); - it("should throw notValidDescriptor if the descriptor is in deprecated state", async () => { + it("should throw notValidDescriptorState if the descriptor is in deprecated state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -385,11 +492,11 @@ describe("publish descriptor", () => { logger: genericLogger, }) ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.deprecated) + notValidDescriptorState(descriptor.id, descriptorState.deprecated) ); }); - it("should throw notValidDescriptor if the descriptor is in suspended state", async () => { + it("should throw notValidDescriptorState if the descriptor is in suspended state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -408,11 +515,11 @@ describe("publish descriptor", () => { logger: genericLogger, }) ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.suspended) + notValidDescriptorState(descriptor.id, descriptorState.suspended) ); }); - it("should throw notValidDescriptor if the descriptor is in archived state", async () => { + it("should throw notValidDescriptorState if the descriptor is in archived state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -431,7 +538,7 @@ describe("publish descriptor", () => { logger: genericLogger, }) ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.archived) + notValidDescriptorState(descriptor.id, descriptorState.archived) ); }); diff --git a/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts b/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts new file mode 100644 index 0000000000..efabfe06e5 --- /dev/null +++ b/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts @@ -0,0 +1,234 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ +import { genericLogger } from "pagopa-interop-commons"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; +import { + Descriptor, + descriptorState, + EService, + toEServiceV2, + operationForbidden, + delegationState, + DescriptorRejectionReason, + EServiceDescriptorDelegatorRejectedV2, + generateId, + delegationKind, +} from "pagopa-interop-models"; +import { beforeAll, vi, afterAll, expect, describe, it } from "vitest"; +import { + eServiceNotFound, + eServiceDescriptorNotFound, + notValidDescriptorState, +} from "../src/model/domain/errors.js"; +import { + addOneEService, + catalogService, + getMockAuthData, + readLastEserviceEvent, + getMockEService, + getMockDescriptor, + getMockDocument, + addOneDelegation, +} from "./utils.js"; + +describe("reject descriptor", () => { + const mockEService = getMockEService(); + const mockDescriptor = getMockDescriptor(); + const mockDocument = getMockDocument(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date()); + }); + afterAll(() => { + vi.useRealTimers(); + }); + it("should write on event-store for the rejection of a waiting for approval descriptor", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.waitingForApproval, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + + const newRejectionReason: DescriptorRejectionReason = { + rejectionReason: "testing", + rejectedAt: new Date(), + }; + + await catalogService.rejectDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + { rejectionReason: newRejectionReason.rejectionReason }, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorDelegatorRejected", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDelegatorRejectedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + state: descriptorState.draft, + rejectionReasons: [newRejectionReason], + }, + ], + }); + + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + }); + + it("should throw eServiceNotFound if the eService doesn't exist", async () => { + await expect( + catalogService.rejectDelegatedEServiceDescriptor( + mockEService.id, + mockDescriptor.id, + { rejectionReason: "test" }, + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(eServiceNotFound(mockEService.id)); + }); + + it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { + const eservice: EService = { + ...mockEService, + descriptors: [], + }; + await addOneEService(eservice); + expect( + catalogService.rejectDelegatedEServiceDescriptor( + eservice.id, + mockDescriptor.id, + { rejectionReason: "test" }, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError( + eServiceDescriptorNotFound(eservice.id, mockDescriptor.id) + ); + }); + + it("should throw operationForbidden if the requester is not the producer", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + expect( + catalogService.rejectDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + + { rejectionReason: "test" }, + { + authData: getMockAuthData(), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + + it("should throw operationForbidden if the requester is the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.rejectDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + + { rejectionReason: "test" }, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + + it.each( + Object.values(descriptorState).filter( + (s) => s !== descriptorState.waitingForApproval + ) + )( + "should throw notValidDescriptorState if the descriptor is in %s state", + async (state) => { + const descriptor: Descriptor = { + ...mockDescriptor, + interface: mockDocument, + state, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + expect( + catalogService.rejectDelegatedEServiceDescriptor( + eservice.id, + descriptor.id, + + { rejectionReason: "test" }, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); + } + ); +}); diff --git a/packages/catalog-process/test/suspendDescriptor.test.ts b/packages/catalog-process/test/suspendDescriptor.test.ts index 321d2d2943..a0a343023e 100644 --- a/packages/catalog-process/test/suspendDescriptor.test.ts +++ b/packages/catalog-process/test/suspendDescriptor.test.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -9,13 +12,15 @@ import { EServiceDescriptorSuspendedV2, toEServiceV2, operationForbidden, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, } from "../src/model/domain/errors.js"; import { addOneEService, @@ -25,6 +30,7 @@ import { getMockEService, getMockDescriptor, getMockDocument, + addOneDelegation, } from "./utils.js"; describe("suspend descriptor", () => { @@ -75,62 +81,77 @@ describe("suspend descriptor", () => { expect(writtenPayload.descriptorId).toEqual(descriptor.id); expect(writtenPayload.eservice).toEqual(expectedEservice); }); - - it("should throw eServiceNotFound if the eservice doesn't exist", () => { - expect( - catalogService.suspendDescriptor(mockEService.id, mockDescriptor.id, { - authData: getMockAuthData(mockEService.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - }) - ).rejects.toThrowError(eServiceNotFound(mockEService.id)); - }); - - it("should throw operationForbidden if the requester is not the producer", async () => { + it("should write on event-store for the suspension of a descriptor (delegate)", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, state: descriptorState.published, }; + const eservice: EService = { ...mockEService, descriptors: [descriptor], }; - await addOneEService(eservice); - expect( - catalogService.suspendDescriptor(eservice.id, descriptor.id, { - authData: getMockAuthData(), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - }) - ).rejects.toThrowError(operationForbidden); - }); - it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { - const eservice: EService = { - ...mockEService, - descriptors: [], - }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + await addOneEService(eservice); + await addOneDelegation(delegation); + + await catalogService.suspendDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceDescriptorSuspended"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorSuspendedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + state: descriptorState.suspended, + suspendedAt: new Date( + Number(writtenPayload.eservice!.descriptors[0]!.suspendedAt) + ), + }, + ], + }); + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + }); + + it("should throw eServiceNotFound if the eservice doesn't exist", () => { expect( - catalogService.suspendDescriptor(eservice.id, mockDescriptor.id, { + catalogService.suspendDescriptor(mockEService.id, mockDescriptor.id, { authData: getMockAuthData(mockEService.producerId), correlationId: generateId(), serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError( - eServiceDescriptorNotFound(eservice.id, mockDescriptor.id) - ); + ).rejects.toThrowError(eServiceNotFound(mockEService.id)); }); - it("should throw notValidDescriptor if the descriptor is in draft state", async () => { + it("should throw operationForbidden if the requester is not the producer", async () => { const descriptor: Descriptor = { ...mockDescriptor, - state: descriptorState.draft, + interface: mockDocument, + state: descriptorState.published, }; const eservice: EService = { ...mockEService, @@ -139,27 +160,32 @@ describe("suspend descriptor", () => { await addOneEService(eservice); expect( catalogService.suspendDescriptor(eservice.id, descriptor.id, { - authData: getMockAuthData(eservice.producerId), + authData: getMockAuthData(), correlationId: generateId(), serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.draft) - ); + ).rejects.toThrowError(operationForbidden); }); - it("should throw notValidDescriptor if the descriptor is in suspended state", async () => { + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, - state: descriptorState.suspended, + state: descriptorState.published, }; const eservice: EService = { ...mockEService, descriptors: [descriptor], }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + await addOneEService(eservice); + await addOneDelegation(delegation); expect( catalogService.suspendDescriptor(eservice.id, descriptor.id, { authData: getMockAuthData(eservice.producerId), @@ -167,31 +193,53 @@ describe("suspend descriptor", () => { serviceName: "", logger: genericLogger, }) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.suspended) - ); + ).rejects.toThrowError(operationForbidden); }); - it("should throw notValidDescriptor if the descriptor is in archived state", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - interface: mockDocument, - state: descriptorState.archived, - }; + it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { const eservice: EService = { ...mockEService, - descriptors: [descriptor], + descriptors: [], }; await addOneEService(eservice); + expect( - catalogService.suspendDescriptor(eservice.id, descriptor.id, { - authData: getMockAuthData(eservice.producerId), + catalogService.suspendDescriptor(eservice.id, mockDescriptor.id, { + authData: getMockAuthData(mockEService.producerId), correlationId: generateId(), serviceName: "", logger: genericLogger, }) ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.archived) + eServiceDescriptorNotFound(eservice.id, mockDescriptor.id) ); }); + + it.each([ + descriptorState.draft, + descriptorState.waitingForApproval, + descriptorState.suspended, + descriptorState.archived, + ])( + "should throw notValidDescriptorState if the descriptor is in %s state", + async (state) => { + const descriptor: Descriptor = { + ...mockDescriptor, + state, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + expect( + catalogService.suspendDescriptor(eservice.id, descriptor.id, { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + }) + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); + } + ); }); diff --git a/packages/catalog-process/test/updateDescriptor.test.ts b/packages/catalog-process/test/updateDescriptor.test.ts index d37f3c302c..d19e36b7f6 100644 --- a/packages/catalog-process/test/updateDescriptor.test.ts +++ b/packages/catalog-process/test/updateDescriptor.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -8,17 +11,20 @@ import { EServiceDescriptorQuotasUpdatedV2, toEServiceV2, operationForbidden, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { catalogApi } from "pagopa-interop-api-clients"; import { expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, inconsistentDailyCalls, } from "../src/model/domain/errors.js"; import { + addOneDelegation, addOneEService, catalogService, getMockAuthData, @@ -32,181 +38,143 @@ describe("update descriptor", () => { const mockEService = getMockEService(); const mockDescriptor = getMockDescriptor(); const mockDocument = getMockDocument(); - it("should write on event-store for the update of a published descriptor", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - state: descriptorState.published, - interface: mockDocument, - publishedAt: new Date(), - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - await addOneEService(eservice); - - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = - { - voucherLifespan: 1000, - dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, - dailyCallsTotal: descriptor.dailyCallsTotal + 10, + it.each([ + descriptorState.published, + descriptorState.suspended, + descriptorState.deprecated, + ])( + "should write on event-store for the update of a descriptor with state %s", + async (descriptorState) => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState, + interface: mockDocument, + publishedAt: new Date(), }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); - const updatedEService: EService = { - ...eservice, - descriptors: [ + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { - ...descriptor, voucherLifespan: 1000, dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, dailyCallsTotal: descriptor.dailyCallsTotal + 10, - }, - ], - }; - const returnedEService = await catalogService.updateDescriptor( - eservice.id, - descriptor.id, - updatedDescriptorQuotasSeed, - { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - } - ); - const writtenEvent = await readLastEserviceEvent(eservice.id); - expect(writtenEvent).toMatchObject({ - stream_id: eservice.id, - version: "1", - type: "EServiceDescriptorQuotasUpdated", - event_version: 2, - }); - const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorQuotasUpdatedV2, - payload: writtenEvent.data, - }); - expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); - expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); - }); + }; - it("should write on event-store for the update of a suspended descriptor", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - state: descriptorState.suspended, - interface: mockDocument, - publishedAt: new Date(), - suspendedAt: new Date(), - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - await addOneEService(eservice); + const updatedEService: EService = { + ...eservice, + descriptors: [ + { + ...descriptor, + voucherLifespan: 1000, + dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, + dailyCallsTotal: descriptor.dailyCallsTotal + 10, + }, + ], + }; + const returnedEService = await catalogService.updateDescriptor( + eservice.id, + descriptor.id, + expectedDescriptorQuotasSeed, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorQuotasUpdatedV2, + payload: writtenEvent.data, + }); + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + } + ); - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = - { - voucherLifespan: 1000, - dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, - dailyCallsTotal: descriptor.dailyCallsTotal + 10, + it.each([ + descriptorState.published, + descriptorState.suspended, + descriptorState.deprecated, + ])( + "should write on event-store for the update of a descriptor with state %s (delegate)", + async (descriptorState) => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState, + interface: mockDocument, + publishedAt: new Date(), + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); - const updatedEService: EService = { - ...eservice, - descriptors: [ + await addOneEService(eservice); + await addOneDelegation(delegation); + + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { - ...descriptor, voucherLifespan: 1000, dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, dailyCallsTotal: descriptor.dailyCallsTotal + 10, - }, - ], - }; - const returnedEService = await catalogService.updateDescriptor( - eservice.id, - descriptor.id, - updatedDescriptorQuotasSeed, - { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - } - ); - const writtenEvent = await readLastEserviceEvent(eservice.id); - expect(writtenEvent).toMatchObject({ - stream_id: eservice.id, - version: "1", - type: "EServiceDescriptorQuotasUpdated", - event_version: 2, - }); - const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorQuotasUpdatedV2, - payload: writtenEvent.data, - }); - expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); - expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); - }); - - it("should write on event-store for the update of an deprecated descriptor", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - state: descriptorState.deprecated, - interface: mockDocument, - publishedAt: new Date(), - deprecatedAt: new Date(), - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - await addOneEService(eservice); + }; - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = - { - voucherLifespan: 1000, - dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, - dailyCallsTotal: descriptor.dailyCallsTotal + 10, + const updatedEService: EService = { + ...eservice, + descriptors: [ + { + ...descriptor, + voucherLifespan: 1000, + dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, + dailyCallsTotal: descriptor.dailyCallsTotal + 10, + }, + ], }; - - const updatedEService: EService = { - ...eservice, - descriptors: [ + const returnedEService = await catalogService.updateDescriptor( + eservice.id, + descriptor.id, + expectedDescriptorQuotasSeed, { - ...descriptor, - voucherLifespan: 1000, - dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, - dailyCallsTotal: descriptor.dailyCallsTotal + 10, - }, - ], - }; - const returnedEService = await catalogService.updateDescriptor( - eservice.id, - descriptor.id, - updatedDescriptorQuotasSeed, - { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - } - ); - const writtenEvent = await readLastEserviceEvent(eservice.id); - expect(writtenEvent).toMatchObject({ - stream_id: eservice.id, - version: "1", - type: "EServiceDescriptorQuotasUpdated", - event_version: 2, - }); - const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorQuotasUpdatedV2, - payload: writtenEvent.data, - }); - expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); - expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); - }); + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptorQuotasUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorQuotasUpdatedV2, + payload: writtenEvent.data, + }); + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + } + ); it("should throw eServiceNotFound if the eservice doesn't exist", () => { - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { voucherLifespan: 1000, dailyCallsPerConsumer: mockDescriptor.dailyCallsPerConsumer + 10, @@ -216,7 +184,7 @@ describe("update descriptor", () => { catalogService.updateDescriptor( mockEService.id, mockDescriptor.id, - updatedDescriptorQuotasSeed, + expectedDescriptorQuotasSeed, { authData: getMockAuthData(mockEService.producerId), correlationId: generateId(), @@ -234,7 +202,7 @@ describe("update descriptor", () => { }; await addOneEService(eservice); - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { voucherLifespan: 1000, dailyCallsPerConsumer: mockDescriptor.dailyCallsPerConsumer + 10, @@ -245,7 +213,7 @@ describe("update descriptor", () => { catalogService.updateDescriptor( mockEService.id, mockDescriptor.id, - updatedDescriptorQuotasSeed, + expectedDescriptorQuotasSeed, { authData: getMockAuthData(mockEService.producerId), correlationId: generateId(), @@ -258,47 +226,50 @@ describe("update descriptor", () => { ); }); - it("should throw notValidDescriptor if the descriptor is in draft state", async () => { - const descriptor: Descriptor = { - ...mockDescriptor, - interface: mockDocument, - state: descriptorState.draft, - }; - const eservice: EService = { - ...mockEService, - descriptors: [descriptor], - }; - await addOneEService(eservice); - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = - { - voucherLifespan: 1000, - dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, - dailyCallsTotal: descriptor.dailyCallsTotal + 10, + it.each([ + descriptorState.draft, + descriptorState.waitingForApproval, + descriptorState.archived, + ])( + "should throw notValidDescriptorState if the descriptor is in %s state", + async (state) => { + const descriptor: Descriptor = { + ...mockDescriptor, + interface: mockDocument, + state, }; - - expect( - catalogService.updateDescriptor( - eservice.id, - descriptor.id, - updatedDescriptorQuotasSeed, + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { - authData: getMockAuthData(eservice.producerId), - correlationId: generateId(), - serviceName: "", - logger: genericLogger, - } - ) - ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState.draft) - ); - }); + voucherLifespan: 1000, + dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, + dailyCallsTotal: descriptor.dailyCallsTotal + 10, + }; - it("should throw notValidDescriptor if the descriptor is in archived state", async () => { + expect( + catalogService.updateDescriptor( + eservice.id, + descriptor.id, + updatedDescriptorQuotasSeed, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(notValidDescriptorState(mockDescriptor.id, state)); + } + ); + + it("should throw operationForbidden if the requester is not the producer", async () => { const descriptor: Descriptor = { ...mockDescriptor, - interface: mockDocument, - state: descriptorState.archived, - archivedAt: new Date(), + state: descriptorState.draft, }; const eservice: EService = { ...mockEService, @@ -306,7 +277,7 @@ describe("update descriptor", () => { }; await addOneEService(eservice); - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { voucherLifespan: 1000, dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, @@ -316,20 +287,18 @@ describe("update descriptor", () => { catalogService.updateDescriptor( eservice.id, descriptor.id, - updatedDescriptorQuotasSeed, + expectedDescriptorQuotasSeed, { - authData: getMockAuthData(eservice.producerId), + authData: getMockAuthData(), correlationId: generateId(), serviceName: "", logger: genericLogger, } ) - ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState.archived) - ); + ).rejects.toThrowError(operationForbidden); }); - it("should throw operationForbidden if the requester is not the producer", async () => { + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { const descriptor: Descriptor = { ...mockDescriptor, state: descriptorState.draft, @@ -338,9 +307,16 @@ describe("update descriptor", () => { ...mockEService, descriptors: [descriptor], }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + await addOneEService(eservice); + await addOneDelegation(delegation); - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { voucherLifespan: 1000, dailyCallsPerConsumer: descriptor.dailyCallsPerConsumer + 10, @@ -350,9 +326,9 @@ describe("update descriptor", () => { catalogService.updateDescriptor( eservice.id, descriptor.id, - updatedDescriptorQuotasSeed, + expectedDescriptorQuotasSeed, { - authData: getMockAuthData(), + authData: getMockAuthData(eservice.producerId), correlationId: generateId(), serviceName: "", logger: genericLogger, @@ -374,7 +350,7 @@ describe("update descriptor", () => { }; await addOneEService(eservice); - const updatedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = + const expectedDescriptorQuotasSeed: catalogApi.UpdateEServiceDescriptorQuotasSeed = { voucherLifespan: 1000, dailyCallsPerConsumer: descriptor.dailyCallsTotal + 11, @@ -384,7 +360,7 @@ describe("update descriptor", () => { catalogService.updateDescriptor( eservice.id, descriptor.id, - updatedDescriptorQuotasSeed, + expectedDescriptorQuotasSeed, { authData: getMockAuthData(eservice.producerId), correlationId: generateId(), diff --git a/packages/catalog-process/test/updateDocument.test.ts b/packages/catalog-process/test/updateDocument.test.ts index 9070d1c5ed..ad56f940dc 100644 --- a/packages/catalog-process/test/updateDocument.test.ts +++ b/packages/catalog-process/test/updateDocument.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -10,12 +13,14 @@ import { operationForbidden, generateId, Document, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, eServiceDocumentNotFound, prettyNameDuplicate, } from "../src/model/domain/errors.js"; @@ -27,6 +32,7 @@ import { getMockDescriptor, getMockDocument, getMockEService, + addOneDelegation, } from "./utils.js"; describe("update Document", () => { @@ -35,7 +41,9 @@ describe("update Document", () => { const mockDocument = getMockDocument(); it.each( Object.values(descriptorState).filter( - (state) => state !== descriptorState.archived + (state) => + state !== descriptorState.archived && + state !== descriptorState.waitingForApproval ) )( "should write on event-store for the update of a document in a descriptor in %s state", @@ -102,6 +110,85 @@ describe("update Document", () => { ); } ); + it.each( + Object.values(descriptorState).filter( + (state) => + state !== descriptorState.archived && + state !== descriptorState.waitingForApproval + ) + )( + "should write on event-store for the update of a document in a descriptor in %s state (delegate)", + async (state) => { + const descriptor: Descriptor = { + ...getMockDescriptor(state), + docs: [mockDocument], + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + const returnedDocument = await catalogService.updateDocument( + eservice.id, + descriptor.id, + mockDocument.id, + { prettyName: "updated prettyName" }, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + docs: [ + { + ...mockDocument, + prettyName: "updated prettyName", + }, + ], + }, + ], + }); + + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceDescriptorDocumentUpdated"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorDocumentUpdatedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.documentId).toEqual(mockDocument.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + expect(writtenPayload.eservice).toEqual( + toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + docs: [returnedDocument], + }, + ], + }) + ); + } + ); it("should throw eServiceNotFound if the eservice doesn't exist", async () => { expect( catalogService.updateDocument( @@ -144,6 +231,39 @@ describe("update Document", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + docs: [mockDocument], + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + expect( + catalogService.updateDocument( + eservice.id, + descriptor.id, + mockDocument.id, + { prettyName: "updated prettyName" }, + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { const eservice: EService = { ...mockEService, @@ -169,10 +289,12 @@ describe("update Document", () => { }); it.each( Object.values(descriptorState).filter( - (state) => state === descriptorState.archived + (state) => + state === descriptorState.archived || + state === descriptorState.waitingForApproval ) )( - "should throw notValidDescriptor if the descriptor is in s% state", + "should throw notValidDescriptorState if the descriptor is in s% state", async (state) => { const descriptor: Descriptor = { ...getMockDescriptor(state), @@ -196,9 +318,7 @@ describe("update Document", () => { logger: genericLogger, } ) - ).rejects.toThrowError( - notValidDescriptor(descriptor.id, descriptorState.archived) - ); + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); } ); it("should throw eServiceDocumentNotFound if the document doesn't exist", async () => { diff --git a/packages/catalog-process/test/updateDraftDescriptor.test.ts b/packages/catalog-process/test/updateDraftDescriptor.test.ts index 6d631b0be3..15dcee92a9 100644 --- a/packages/catalog-process/test/updateDraftDescriptor.test.ts +++ b/packages/catalog-process/test/updateDraftDescriptor.test.ts @@ -1,7 +1,10 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; import { catalogApi } from "pagopa-interop-api-clients"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -11,12 +14,14 @@ import { EServiceDraftDescriptorUpdatedV2, toEServiceV2, operationForbidden, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, inconsistentDailyCalls, attributeNotFound, } from "../src/model/domain/errors.js"; @@ -30,6 +35,7 @@ import { getMockEService, getMockDocument, buildUpdateDescriptorSeed, + addOneDelegation, } from "./utils.js"; describe("update draft descriptor", () => { @@ -55,7 +61,7 @@ describe("update draft descriptor", () => { }; await addOneAttribute(attribute); - const updatedDescriptorSeed: catalogApi.UpdateEServiceDescriptorSeed = { + const expectedDescriptorSeed: catalogApi.UpdateEServiceDescriptorSeed = { ...buildUpdateDescriptorSeed(descriptor), dailyCallsTotal: 200, attributes: { @@ -86,7 +92,7 @@ describe("update draft descriptor", () => { await catalogService.updateDraftDescriptor( eservice.id, descriptor.id, - updatedDescriptorSeed, + expectedDescriptorSeed, { authData: getMockAuthData(eservice.producerId), correlationId: generateId(), @@ -107,6 +113,84 @@ describe("update draft descriptor", () => { }); expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); }); + it("should write on event-store for the update of a draft descriptor (delegate)", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + const attribute: Attribute = { + name: "Attribute name", + id: generateId(), + kind: "Declared", + description: "Attribute Description", + creationTime: new Date(), + }; + await addOneAttribute(attribute); + + const expectedDescriptorSeed: catalogApi.UpdateEServiceDescriptorSeed = { + ...buildUpdateDescriptorSeed(descriptor), + dailyCallsTotal: 200, + attributes: { + certified: [], + declared: [ + [{ id: attribute.id, explicitAttributeVerification: false }], + ], + verified: [], + }, + }; + + const updatedEService: EService = { + ...eservice, + descriptors: [ + { + ...descriptor, + dailyCallsTotal: 200, + attributes: { + certified: [], + declared: [ + [{ id: attribute.id, explicitAttributeVerification: false }], + ], + verified: [], + }, + }, + ], + }; + await catalogService.updateDraftDescriptor( + eservice.id, + descriptor.id, + expectedDescriptorSeed, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDraftDescriptorUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDraftDescriptorUpdatedV2, + payload: writtenEvent.data, + }); + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + }); it("should throw eServiceNotFound if the eservice doesn't exist", () => { const descriptor: Descriptor = { @@ -153,7 +237,7 @@ describe("update draft descriptor", () => { ); }); - it("should throw notValidDescriptor if the descriptor is in published state", async () => { + it("should throw notValidDescriptorState if the descriptor is in published state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -178,11 +262,11 @@ describe("update draft descriptor", () => { } ) ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState.published) + notValidDescriptorState(mockDescriptor.id, descriptorState.published) ); }); - it("should throw notValidDescriptor if the descriptor is in deprecated state", async () => { + it("should throw notValidDescriptorState if the descriptor is in deprecated state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -207,11 +291,11 @@ describe("update draft descriptor", () => { } ) ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState.deprecated) + notValidDescriptorState(mockDescriptor.id, descriptorState.deprecated) ); }); - it("should throw notValidDescriptor if the descriptor is in suspended state", async () => { + it("should throw notValidDescriptorState if the descriptor is in suspended state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -236,11 +320,11 @@ describe("update draft descriptor", () => { } ) ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState.suspended) + notValidDescriptorState(mockDescriptor.id, descriptorState.suspended) ); }); - it("should throw notValidDescriptor if the descriptor is in archived state", async () => { + it("should throw notValidDescriptorState if the descriptor is in archived state", async () => { const descriptor: Descriptor = { ...mockDescriptor, interface: mockDocument, @@ -265,7 +349,7 @@ describe("update draft descriptor", () => { } ) ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState.archived) + notValidDescriptorState(mockDescriptor.id, descriptorState.archived) ); }); @@ -280,7 +364,7 @@ describe("update draft descriptor", () => { }; await addOneEService(eservice); - const updatedDescriptor = { + const expectedDescriptor = { ...descriptor, dailyCallsTotal: 200, }; @@ -288,7 +372,7 @@ describe("update draft descriptor", () => { catalogService.updateDraftDescriptor( eservice.id, descriptor.id, - buildUpdateDescriptorSeed(updatedDescriptor), + buildUpdateDescriptorSeed(expectedDescriptor), { authData: getMockAuthData(), correlationId: generateId(), @@ -299,6 +383,44 @@ describe("update draft descriptor", () => { ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + const expectedDescriptor = { + ...descriptor, + dailyCallsTotal: 200, + }; + expect( + catalogService.updateDraftDescriptor( + eservice.id, + descriptor.id, + buildUpdateDescriptorSeed(expectedDescriptor), + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + it("should throw inconsistentDailyCalls if dailyCallsPerConsumer is greater than dailyCallsTotal", async () => { const descriptor: Descriptor = { ...mockDescriptor, @@ -310,7 +432,7 @@ describe("update draft descriptor", () => { }; await addOneEService(eservice); - const updatedDescriptor: Descriptor = { + const expectedDescriptor: Descriptor = { ...descriptor, dailyCallsPerConsumer: 100, dailyCallsTotal: 50, @@ -319,7 +441,7 @@ describe("update draft descriptor", () => { catalogService.updateDraftDescriptor( eservice.id, descriptor.id, - buildUpdateDescriptorSeed(updatedDescriptor), + buildUpdateDescriptorSeed(expectedDescriptor), { authData: getMockAuthData(eservice.producerId), correlationId: generateId(), diff --git a/packages/catalog-process/test/updateEservice.test.ts b/packages/catalog-process/test/updateEservice.test.ts index bf454c4542..f54f79df27 100644 --- a/packages/catalog-process/test/updateEservice.test.ts +++ b/packages/catalog-process/test/updateEservice.test.ts @@ -2,6 +2,7 @@ import { genericLogger, fileManagerDeleteError } from "pagopa-interop-commons"; import { decodeProtobufPayload, + getMockDelegation, getMockValidRiskAnalysis, randomArrayItem, } from "pagopa-interop-commons-test/index.js"; @@ -14,6 +15,8 @@ import { eserviceMode, operationForbidden, generateId, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { vi, expect, describe, it } from "vitest"; import { @@ -31,6 +34,7 @@ import { getMockDocument, getMockDescriptor, getMockEService, + addOneDelegation, } from "./utils.js"; describe("update eService", () => { @@ -254,6 +258,52 @@ describe("update eService", () => { expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); }); + it("should write on event-store for the update of an eService (delegate)", async () => { + const updatedDescription = "eservice new description"; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + const returnedEService = await catalogService.updateEService( + mockEService.id, + { + name: mockEService.name, + description: updatedDescription, + technology: "REST", + mode: "DELIVER", + }, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const updatedEService: EService = { + ...mockEService, + description: updatedDescription, + }; + + const writtenEvent = await readLastEserviceEvent(mockEService.id); + expect(writtenEvent).toMatchObject({ + stream_id: mockEService.id, + version: "1", + type: "DraftEServiceUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: DraftEServiceUpdatedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + }); it("should write on event-store for the update of an eService (update mode to DELIVER so risk analysis has to be deleted)", async () => { const riskAnalysis = getMockValidRiskAnalysis("PA"); @@ -345,6 +395,35 @@ describe("update eService", () => { ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + + expect( + catalogService.updateEService( + mockEService.id, + { + name: "eservice new name", + description: "eservice description", + technology: "REST", + mode: "DELIVER", + }, + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + it("should throw eServiceDuplicate if the updated name is already in use, case insensitive", async () => { const eservice1: EService = { ...mockEService, diff --git a/packages/catalog-process/test/updateEserviceDescription.test.ts b/packages/catalog-process/test/updateEserviceDescription.test.ts index da5ddd5c0b..99906570e9 100644 --- a/packages/catalog-process/test/updateEserviceDescription.test.ts +++ b/packages/catalog-process/test/updateEserviceDescription.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -8,7 +11,9 @@ import { toEServiceV2, operationForbidden, EServiceDescriptionUpdatedV2, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { @@ -23,6 +28,7 @@ import { getMockDocument, getMockDescriptor, getMockEService, + addOneDelegation, } from "./utils.js"; describe("update eService description", () => { @@ -69,6 +75,56 @@ describe("update eService description", () => { expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); }); + it("should write on event-store for the update of the eService description (delegate)", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(descriptorState.published), + interface: getMockDocument(), + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + const updatedDescription = "eservice new description"; + const returnedEService = await catalogService.updateEServiceDescription( + eservice.id, + updatedDescription, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const updatedEService: EService = { + ...eservice, + description: updatedDescription, + }; + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceDescriptionUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptionUpdatedV2, + payload: writtenEvent.data, + }); + + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + }); it("should throw eServiceNotFound if the eservice doesn't exist", async () => { const eservice = getMockEService(); @@ -103,6 +159,30 @@ describe("update eService description", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const eservice = getMockEService(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + expect( + catalogService.updateEServiceDescription( + eservice.id, + "eservice new description", + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("shoudl throw eserviceWithoutValidDescriptors if the eservice doesn't have any descriptors", async () => { const eservice = getMockEService(); await addOneEService(eservice); diff --git a/packages/catalog-process/test/updateRiskAnalysis.test.ts b/packages/catalog-process/test/updateRiskAnalysis.test.ts index fa00e2386b..050a9cff2d 100644 --- a/packages/catalog-process/test/updateRiskAnalysis.test.ts +++ b/packages/catalog-process/test/updateRiskAnalysis.test.ts @@ -10,6 +10,7 @@ import { getMockTenant, getMockValidRiskAnalysis, decodeProtobufPayload, + getMockDelegation, } from "pagopa-interop-commons-test/index.js"; import { TenantKind, @@ -27,6 +28,8 @@ import { generateId, operationForbidden, RiskAnalysisId, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { catalogApi } from "pagopa-interop-api-clients"; import { expect, describe, it } from "vitest"; @@ -49,6 +52,7 @@ import { readLastEserviceEvent, getMockDescriptor, getMockEService, + addOneDelegation, } from "./utils.js"; describe("update risk analysis", () => { @@ -172,6 +176,131 @@ describe("update risk analysis", () => { expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEservice)); }); + it("should write on event-store for the update of a risk analysis (delegate)", async () => { + const producerTenantKind: TenantKind = randomArrayItem( + Object.values(tenantKind) + ); + const producer: Tenant = { + ...getMockTenant(), + kind: producerTenantKind, + }; + + const riskAnalysis = getMockValidRiskAnalysis(producerTenantKind); + + const eservice: EService = { + ...mockEService, + producerId: producer.id, + mode: eserviceMode.receive, + descriptors: [ + { + ...mockDescriptor, + state: descriptorState.draft, + }, + ], + riskAnalysis: [riskAnalysis], + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneTenant(producer); + await addOneEService(eservice); + await addOneDelegation(delegation); + + const riskAnalysisSeed: catalogApi.EServiceRiskAnalysisSeed = + buildRiskAnalysisSeed(riskAnalysis); + + const riskAnalysisUpdatedSeed: catalogApi.EServiceRiskAnalysisSeed = { + ...riskAnalysisSeed, + riskAnalysisForm: { + ...riskAnalysisSeed.riskAnalysisForm, + answers: { + ...riskAnalysisSeed.riskAnalysisForm.answers, + purpose: ["OTHER"], // we modify the purpose field, present in the mock for all tenant kinds + otherPurpose: ["updated other purpose"], // we add a new field + ruleOfLawText: [], // we remove the ruleOfLawText field, present in the mock for all tenant kinds + }, + }, + }; + + await catalogService.updateRiskAnalysis( + eservice.id, + riskAnalysis.id, + riskAnalysisUpdatedSeed, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent).toMatchObject({ + stream_id: eservice.id, + version: "1", + type: "EServiceRiskAnalysisUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceRiskAnalysisUpdatedV2, + payload: writtenEvent.data, + }); + + const updatedEservice: EService = { + ...eservice, + riskAnalysis: [ + { + ...riskAnalysis, + name: riskAnalysisUpdatedSeed.name, + riskAnalysisForm: { + ...riskAnalysis.riskAnalysisForm, + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.id + ), + multiAnswers: riskAnalysis.riskAnalysisForm.multiAnswers.map( + (multiAnswer) => ({ + ...multiAnswer, + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.multiAnswers.find( + (ma) => ma.key === multiAnswer.key + )!.id + ), + }) + ), + singleAnswers: riskAnalysis.riskAnalysisForm.singleAnswers + .filter((singleAnswer) => singleAnswer.key !== "ruleOfLawText") + .map((singleAnswer) => ({ + ...singleAnswer, + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === singleAnswer.key + )!.id + ), + value: + singleAnswer.key === "purpose" ? "OTHER" : singleAnswer.value, + })) + .concat([ + { + key: "otherPurpose", + value: "updated other purpose", + id: unsafeBrandId( + writtenPayload.eservice!.riskAnalysis[0]!.riskAnalysisForm!.singleAnswers.find( + (sa) => sa.key === "otherPurpose" + )!.id + ), + }, + ]), + }, + }, + ], + }; + + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEservice)); + }); it("should throw eServiceNotFound if the eservice doesn't exist", async () => { expect( catalogService.updateRiskAnalysis( @@ -203,6 +332,30 @@ describe("update risk analysis", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + + expect( + catalogService.updateRiskAnalysis( + mockEService.id, + generateId(), + buildRiskAnalysisSeed(getMockValidRiskAnalysis(tenantKind.PA)), + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("should throw eserviceNotInDraftState if the eservice is not in draft state", async () => { const eservice: EService = { ...mockEService, diff --git a/packages/catalog-process/test/uploadDocument.test.ts b/packages/catalog-process/test/uploadDocument.test.ts index 34c6ae6cd9..1f93e27eaa 100644 --- a/packages/catalog-process/test/uploadDocument.test.ts +++ b/packages/catalog-process/test/uploadDocument.test.ts @@ -10,14 +10,19 @@ import { unsafeBrandId, operationForbidden, Document, + delegationState, generateId, + delegationKind, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { eServiceNotFound, eServiceDescriptorNotFound, - notValidDescriptor, + notValidDescriptorState, interfaceAlreadyExists, prettyNameDuplicate, } from "../src/model/domain/errors.js"; @@ -31,6 +36,7 @@ import { getMockDocument, getMockEService, buildDocumentSeed, + addOneDelegation, } from "./utils.js"; describe("upload Document", () => { @@ -39,7 +45,9 @@ describe("upload Document", () => { const mockDocument = getMockDocument(); it.each( Object.values(descriptorState).filter( - (state) => state !== descriptorState.archived + (state) => + state !== descriptorState.archived && + state !== descriptorState.waitingForApproval ) )( "should write on event-store for the upload of a document when descriptor state is %s", @@ -102,6 +110,80 @@ describe("upload Document", () => { expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); } ); + it.each( + Object.values(descriptorState).filter( + (state) => + state !== descriptorState.archived && + state !== descriptorState.waitingForApproval + ) + )( + "should write on event-store for the upload of a document when descriptor state is %s (delegate)", + async (state) => { + const descriptor: Descriptor = { + ...getMockDescriptor(state), + serverUrls: [], + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneDelegation(delegation); + + const returnedEService = await catalogService.uploadDocument( + eservice.id, + descriptor.id, + buildInterfaceSeed(), + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const writtenEvent = await readLastEserviceEvent(eservice.id); + expect(writtenEvent.stream_id).toBe(eservice.id); + expect(writtenEvent.version).toBe("1"); + expect(writtenEvent.type).toBe("EServiceDescriptorInterfaceAdded"); + expect(writtenEvent.event_version).toBe(2); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorInterfaceDeletedV2, + payload: writtenEvent.data, + }); + + const expectedEservice = toEServiceV2({ + ...eservice, + descriptors: [ + { + ...descriptor, + interface: { + ...mockDocument, + id: unsafeBrandId( + writtenPayload.eservice!.descriptors[0]!.interface!.id + ), + checksum: + writtenPayload.eservice!.descriptors[0]!.interface!.checksum, + uploadDate: new Date( + writtenPayload.eservice!.descriptors[0]!.interface!.uploadDate + ), + }, + serverUrls: ["pagopa.it"], + }, + ], + }); + + expect(writtenPayload.descriptorId).toEqual(descriptor.id); + expect(writtenPayload.eservice).toEqual(expectedEservice); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + } + ); it("should throw eServiceNotFound if the eservice doesn't exist", () => { expect( catalogService.uploadDocument( @@ -142,6 +224,63 @@ describe("upload Document", () => { ) ).rejects.toThrowError(operationForbidden); }); + it("should throw operationForbidden if the requester is not the producer", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + await addOneEService(eservice); + + expect( + catalogService.uploadDocument( + eservice.id, + descriptor.id, + buildInterfaceSeed(), + { + authData: getMockAuthData(), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const descriptor: Descriptor = { + ...mockDescriptor, + state: descriptorState.draft, + }; + const eservice: EService = { + ...mockEService, + descriptors: [descriptor], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eservice.id, + state: delegationState.active, + }); + + await addOneEService(mockEService); + await addOneDelegation(delegation); + + expect( + catalogService.uploadDocument( + eservice.id, + descriptor.id, + buildInterfaceSeed(), + { + authData: getMockAuthData(eservice.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it("should throw eServiceDescriptorNotFound if the descriptor doesn't exist", async () => { const eservice: EService = { ...mockEService, @@ -167,10 +306,12 @@ describe("upload Document", () => { it.each( Object.values(descriptorState).filter( - (state) => state === descriptorState.archived + (state) => + state === descriptorState.archived || + state === descriptorState.waitingForApproval ) )( - "should throw notValidDescriptor if the descriptor is in %s state", + "should throw notValidDescriptorState if the descriptor is in %s state", async (state) => { const descriptor: Descriptor = { ...getMockDescriptor(state), @@ -192,7 +333,7 @@ describe("upload Document", () => { logger: genericLogger, } ) - ).rejects.toThrowError(notValidDescriptor(descriptor.id, state)); + ).rejects.toThrowError(notValidDescriptorState(descriptor.id, state)); } ); it("should throw interfaceAlreadyExists if the descriptor already contains an interface", async () => { diff --git a/packages/catalog-process/test/utils.ts b/packages/catalog-process/test/utils.ts index f0e220524c..707be43610 100644 --- a/packages/catalog-process/test/utils.ts +++ b/packages/catalog-process/test/utils.ts @@ -27,6 +27,7 @@ import { toReadModelTenant, toReadModelAgreement, DescriptorState, + Delegation, } from "pagopa-interop-models"; import { ReadEvent, @@ -54,6 +55,7 @@ export const agreements = readModelRepository.agreements; export const eservices = readModelRepository.eservices; export const tenants = readModelRepository.tenants; export const attributes = readModelRepository.attributes; +export const delegations = readModelRepository.delegations; export const readModelService = readModelServiceBuilder(readModelRepository); @@ -283,6 +285,12 @@ export const addOneAgreement = async (agreement: Agreement): Promise => { await writeInReadmodel(toReadModelAgreement(agreement), agreements); }; +export const addOneDelegation = async ( + delegation: Delegation +): Promise => { + await writeInReadmodel(delegation, delegations); +}; + export const readLastEserviceEvent = async ( eserviceId: EServiceId ): Promise> => diff --git a/packages/catalog-readmodel-writer/src/consumerServiceV2.ts b/packages/catalog-readmodel-writer/src/consumerServiceV2.ts index 9ac9807ec9..9d6b92b040 100644 --- a/packages/catalog-readmodel-writer/src/consumerServiceV2.ts +++ b/packages/catalog-readmodel-writer/src/consumerServiceV2.ts @@ -41,6 +41,9 @@ export async function handleMessageV2( { type: "EServiceRiskAnalysisUpdated" }, { type: "EServiceRiskAnalysisDeleted" }, { type: "EServiceDescriptionUpdated" }, + { type: "EServiceDescriptorDelegateSubmitted" }, + { type: "EServiceDescriptorDelegatorApproved" }, + { type: "EServiceDescriptorDelegatorRejected" }, async (message) => await eservices.updateOne( { diff --git a/packages/catalog-readmodel-writer/test/catalogReadmodelWriter.integration.test.ts b/packages/catalog-readmodel-writer/test/catalogReadmodelWriter.integration.test.ts index 4df63491ba..85d99d9d8c 100644 --- a/packages/catalog-readmodel-writer/test/catalogReadmodelWriter.integration.test.ts +++ b/packages/catalog-readmodel-writer/test/catalogReadmodelWriter.integration.test.ts @@ -227,14 +227,14 @@ describe("database test", async () => { descriptors: [descriptor], }; await writeInReadmodel(toReadModelEService(eservice), eservices, 1); - const updatedDescriptor = { + const expectedDescriptor = { ...descriptor, attributes, }; const updatedEService: EService = { ...mockEService, attributes: undefined, - descriptors: [updatedDescriptor], + descriptors: [expectedDescriptor], }; const payload: MovedAttributesFromEserviceToDescriptorsV1 = { eservice: toEServiceV1(updatedEService), diff --git a/packages/commons-test/src/protobufConvertersToV1/catalogProtobufConverterToV1.ts b/packages/commons-test/src/protobufConvertersToV1/catalogProtobufConverterToV1.ts index 0aa0784697..d6e5df0ef0 100644 --- a/packages/commons-test/src/protobufConvertersToV1/catalogProtobufConverterToV1.ts +++ b/packages/commons-test/src/protobufConvertersToV1/catalogProtobufConverterToV1.ts @@ -39,6 +39,7 @@ export const toEServiceDescriptorStateV1 = ( .with("Archived", () => EServiceDescriptorStateV1.ARCHIVED) .with("Published", () => EServiceDescriptorStateV1.PUBLISHED) .with("Deprecated", () => EServiceDescriptorStateV1.DEPRECATED) + .with("WaitingForApproval", () => EServiceDescriptorStateV1.DRAFT) .exhaustive(); export const toEServiceTechnologyV1 = ( diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 6b72aa572a..80c01f9ec7 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -1,3 +1,4 @@ +/* eslint-disable fp/no-delete */ import crypto from "crypto"; import { fail } from "assert"; import { generateMock } from "@anatine/zod-mock"; @@ -67,6 +68,7 @@ import { PlatformStatesClientPK, PlatformStatesClientEntry, makePlatformStatesClientPK, + AgreementStamps, DelegationKind, unsafeBrandId, UserId, @@ -225,6 +227,18 @@ export const getMockTenantMail = ( address: generateMock(z.string().email()), }); +export const getMockAgreementStamps = (): AgreementStamps => { + const stamps = generateMock(AgreementStamps); + delete stamps.submission?.delegationId; + delete stamps.activation?.delegationId; + delete stamps.rejection?.delegationId; + delete stamps.suspensionByConsumer?.delegationId; + delete stamps.suspensionByProducer?.delegationId; + delete stamps.upgrade?.delegationId; + delete stamps.archiving?.delegationId; + return stamps; +}; + export const getMockAgreement = ( eserviceId: EServiceId = generateId(), consumerId: TenantId = generateId(), @@ -234,6 +248,7 @@ export const getMockAgreement = ( eserviceId, consumerId, state, + stamps: getMockAgreementStamps(), }); export const getMockAttribute = ( diff --git a/packages/compute-agreements-consumer/src/converters.ts b/packages/compute-agreements-consumer/src/converters.ts index 40b1e6f061..791c59cded 100644 --- a/packages/compute-agreements-consumer/src/converters.ts +++ b/packages/compute-agreements-consumer/src/converters.ts @@ -38,6 +38,7 @@ function toApiCompactTenantVerifiedAttribute( extensionDate: v.extensionDate?.toISOString(), verificationDate: v.verificationDate?.toISOString(), expirationDate: v.expirationDate?.toISOString(), + delegationId: v.delegationId, })), revokedBy: attr.revokedBy.map((v) => ({ id: v.id, @@ -45,6 +46,7 @@ function toApiCompactTenantVerifiedAttribute( verificationDate: v.verificationDate.toISOString(), revocationDate: v.revocationDate.toISOString(), expirationDate: v.expirationDate?.toISOString(), + delegationId: v.delegationId, })), }; } diff --git a/packages/datalake-data-export/src/services/readModelService.ts b/packages/datalake-data-export/src/services/readModelService.ts index 2dc85fda11..53d1e28708 100644 --- a/packages/datalake-data-export/src/services/readModelService.ts +++ b/packages/datalake-data-export/src/services/readModelService.ts @@ -52,7 +52,9 @@ export function readModelServiceBuilder( ExportedEService.parse({ ...data, descriptors: data.descriptors.filter( - (descriptor) => descriptor.state !== "Draft" + (descriptor) => + descriptor.state !== "Draft" && + descriptor.state !== "WaitingForApproval" ), }) ) diff --git a/packages/datalake-data-export/test/read-model-queries-service.test.ts b/packages/datalake-data-export/test/read-model-queries-service.test.ts index 497bad17da..734ac13c24 100644 --- a/packages/datalake-data-export/test/read-model-queries-service.test.ts +++ b/packages/datalake-data-export/test/read-model-queries-service.test.ts @@ -64,7 +64,9 @@ describe("read-model-queries.service", () => { describe("getEServices", async () => { const validEserviceDescriptorStates = Object.values(descriptorState).filter( - (state) => state !== descriptorState.draft + (state) => + state !== descriptorState.draft && + state !== descriptorState.waitingForApproval ); it("should return all eServices", async () => { @@ -123,6 +125,37 @@ describe("read-model-queries.service", () => { expect(result.at(0)?.descriptors.at(0)?.state).toEqual("Published"); }); + it("should not return waiting for approval descriptors in the e-service", async () => { + const eservicesData = [ + getMockEService(generateId(), generateId(), [ + { + ...getMockDescriptor(), + id: unsafeBrandId("a9c705d9-ecdb-47ff-bcd2-667495b111f2"), + version: "2", + state: descriptorState.published, + }, + { + ...getMockDescriptor(), + id: unsafeBrandId("a9c705d9-ecdb-47ff-bcd2-667495b111f3"), + state: descriptorState.waitingForApproval, + version: "1", + attributes: { + certified: [], + verified: [], + declared: [], + }, + }, + ]), + ].map(toReadModelEService); + + await seedCollection(eservicesData, eservices); + + const result = await readModelService.getEServices(); + expect(result).toHaveLength(eservicesData.length); + expect(result.at(0)?.descriptors).toHaveLength(1); + expect(result.at(0)?.descriptors.at(0)?.state).toEqual("Published"); + }); + it("should return empty array if no eServices are found", async () => { const result = await readModelService.getEServices(); expect(result).toHaveLength(0); diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts index da5151180c..396755c684 100644 --- a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts +++ b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts @@ -11,25 +11,29 @@ export async function exportInterfaceV2( logger: Logger ): Promise { await match(decodedMsg) - .with({ type: "EServiceDescriptorPublished" }, async ({ data }) => { - if (data.eservice) { - logger.info( - `Processing ${decodedMsg.type} message - Partition number: ${originalPayload.partition} - Offset: ${originalPayload.message.offset}` - ); - const eservice = fromEServiceV2(data.eservice); - const publishedDescriptor = eservice.descriptors.find( - (d) => d.id === data.descriptorId - ); - if (publishedDescriptor) { - await exportInterface( - eservice.id, - publishedDescriptor, - fileManager, - logger + .with( + { type: "EServiceDescriptorPublished" }, + { type: "EServiceDescriptorDelegatorApproved" }, + async ({ data }) => { + if (data.eservice) { + logger.info( + `Processing ${decodedMsg.type} message - Partition number: ${originalPayload.partition} - Offset: ${originalPayload.message.offset}` + ); + const eservice = fromEServiceV2(data.eservice); + const publishedDescriptor = eservice.descriptors.find( + (d) => d.id === data.descriptorId ); + if (publishedDescriptor) { + await exportInterface( + eservice.id, + publishedDescriptor, + fileManager, + logger + ); + } } } - }) + ) .with( { type: "EServiceAdded" }, { type: "DraftEServiceUpdated" }, @@ -52,6 +56,8 @@ export async function exportInterfaceV2( { type: "EServiceRiskAnalysisUpdated" }, { type: "EServiceRiskAnalysisDeleted" }, { type: "EServiceDescriptionUpdated" }, + { type: "EServiceDescriptorDelegateSubmitted" }, + { type: "EServiceDescriptorDelegatorRejected" }, () => undefined ) .exhaustive(); diff --git a/packages/models/proto/v2/agreement/agreement.proto b/packages/models/proto/v2/agreement/agreement.proto index 0f67571e75..77b4a0cb08 100644 --- a/packages/models/proto/v2/agreement/agreement.proto +++ b/packages/models/proto/v2/agreement/agreement.proto @@ -61,6 +61,7 @@ enum AgreementStateV2 { message AgreementStampV2 { string who = 1; int64 when = 2; + optional string delegationId = 3; } message AgreementStampsV2 { diff --git a/packages/models/proto/v2/eservice/eservice.proto b/packages/models/proto/v2/eservice/eservice.proto index 4ea2a1c389..3645b9f915 100644 --- a/packages/models/proto/v2/eservice/eservice.proto +++ b/packages/models/proto/v2/eservice/eservice.proto @@ -57,6 +57,11 @@ message EServiceAttributesV2 { repeated EServiceAttributeV2 verified = 3; } +message DescriptorRejectionReasonV2 { + string rejectionReason = 1; + int64 rejectedAt = 2; +} + message EServiceDescriptorV2 { string id = 1; int64 version = 2; @@ -76,6 +81,7 @@ message EServiceDescriptorV2 { optional int64 deprecatedAt = 16; optional int64 archivedAt = 17; EServiceAttributesV2 attributes = 18; + repeated DescriptorRejectionReasonV2 rejectionReasons = 19; } message EServiceDocumentV2 { @@ -94,6 +100,7 @@ enum EServiceDescriptorStateV2 { DEPRECATED = 2; SUSPENDED = 3; ARCHIVED = 4; + WAITING_FOR_APPROVAL = 5; } enum EServiceTechnologyV2 { diff --git a/packages/models/proto/v2/eservice/events.proto b/packages/models/proto/v2/eservice/events.proto index 4506ae3f5e..d014fd4ac7 100644 --- a/packages/models/proto/v2/eservice/events.proto +++ b/packages/models/proto/v2/eservice/events.proto @@ -117,3 +117,18 @@ message EServiceRiskAnalysisDeletedV2 { message EServiceDescriptionUpdatedV2 { EServiceV2 eservice = 1; } + +message EServiceDescriptorDelegateSubmittedV2 { + string descriptorId = 1; + EServiceV2 eservice = 2; +} + +message EServiceDescriptorDelegatorApprovedV2 { + string descriptorId = 1; + EServiceV2 eservice = 2; +} + +message EServiceDescriptorDelegatorRejectedV2 { + string descriptorId = 1; + EServiceV2 eservice = 2; +} diff --git a/packages/models/proto/v2/tenant/tenant.proto b/packages/models/proto/v2/tenant/tenant.proto index 75aaf6d2bb..b2e2d3aee1 100644 --- a/packages/models/proto/v2/tenant/tenant.proto +++ b/packages/models/proto/v2/tenant/tenant.proto @@ -63,6 +63,7 @@ message TenantVerifierV2 { int64 verificationDate = 2; optional int64 expirationDate = 3; optional int64 extensionDate = 4; + optional string delegationId = 5; } message TenantRevokerV2 { @@ -71,6 +72,7 @@ message TenantRevokerV2 { optional int64 expirationDate = 3; optional int64 extensionDate = 4; int64 revocationDate = 5; + optional string delegationId = 6; } message ExternalIdV2 { diff --git a/packages/models/src/agreement/agreement.ts b/packages/models/src/agreement/agreement.ts index b13d62d8dc..e57d91c192 100644 --- a/packages/models/src/agreement/agreement.ts +++ b/packages/models/src/agreement/agreement.ts @@ -3,6 +3,7 @@ import { AgreementDocumentId, AgreementId, AttributeId, + DelegationId, DescriptorId, EServiceId, TenantId, @@ -39,6 +40,7 @@ export type AgreementDocument = z.infer; export const AgreementStamp = z.object({ who: UserId, + delegationId: DelegationId.optional(), when: z.coerce.date(), }); export type AgreementStamp = z.infer; @@ -115,4 +117,9 @@ export type AgreementContractPDFPayload = { attributeId: string; expirationDate: string | undefined; }>; + producerDelegationId?: string; + producerDelegatorName?: string; + producerDelegatorIpaCode?: string; + producerDelegateName?: string; + producerDelegateIpaCode?: string; }; diff --git a/packages/models/src/agreement/protobufConverterFromV2.ts b/packages/models/src/agreement/protobufConverterFromV2.ts index afd3d2fcba..61ac86cd27 100644 --- a/packages/models/src/agreement/protobufConverterFromV2.ts +++ b/packages/models/src/agreement/protobufConverterFromV2.ts @@ -1,4 +1,4 @@ -import { unsafeBrandId } from "../brandedIds.js"; +import { DelegationId, unsafeBrandId } from "../brandedIds.js"; import { AgreementDocumentV2, AgreementStampV2, @@ -31,32 +31,22 @@ export const fromAgreementStampV2 = ( ? { who: unsafeBrandId(input.who), when: bigIntToDate(input.when), + delegationId: input.delegationId + ? unsafeBrandId(input.delegationId) + : undefined, } : undefined; export const fromAgreementStampsV2 = ( input: AgreementStampsV2 | undefined ): AgreementStamps => ({ - ...input, - submission: input?.submission - ? fromAgreementStampV2(input.submission) - : undefined, - activation: input?.activation - ? fromAgreementStampV2(input.activation) - : undefined, - rejection: input?.rejection - ? fromAgreementStampV2(input.rejection) - : undefined, - suspensionByProducer: input?.suspensionByProducer - ? fromAgreementStampV2(input.suspensionByProducer) - : undefined, - suspensionByConsumer: input?.suspensionByConsumer - ? fromAgreementStampV2(input.suspensionByConsumer) - : undefined, - upgrade: input?.upgrade ? fromAgreementStampV2(input.upgrade) : undefined, - archiving: input?.archiving - ? fromAgreementStampV2(input.archiving) - : undefined, + submission: fromAgreementStampV2(input?.submission), + activation: fromAgreementStampV2(input?.activation), + rejection: fromAgreementStampV2(input?.rejection), + suspensionByProducer: fromAgreementStampV2(input?.suspensionByProducer), + suspensionByConsumer: fromAgreementStampV2(input?.suspensionByConsumer), + upgrade: fromAgreementStampV2(input?.upgrade), + archiving: fromAgreementStampV2(input?.archiving), }); export const fromAgreementStateV2 = ( diff --git a/packages/models/src/eservice/eservice.ts b/packages/models/src/eservice/eservice.ts index 4d6235171e..85e2fed90d 100644 --- a/packages/models/src/eservice/eservice.ts +++ b/packages/models/src/eservice/eservice.ts @@ -21,6 +21,7 @@ export const descriptorState = { deprecated: "Deprecated", suspended: "Suspended", archived: "Archived", + waitingForApproval: "WaitingForApproval", } as const; export const DescriptorState = z.enum([ Object.values(descriptorState)[0], @@ -62,6 +63,14 @@ export const Document = z.object({ }); export type Document = z.infer; +export const DescriptorRejectionReason = z.object({ + rejectionReason: z.string(), + rejectedAt: z.coerce.date(), +}); +export type DescriptorRejectionReason = z.infer< + typeof DescriptorRejectionReason +>; + export const Descriptor = z.object({ id: DescriptorId, version: z.string(), @@ -81,6 +90,7 @@ export const Descriptor = z.object({ deprecatedAt: z.coerce.date().optional(), archivedAt: z.coerce.date().optional(), attributes: EServiceAttributes, + rejectionReasons: z.array(DescriptorRejectionReason).optional(), }); export type Descriptor = z.infer; diff --git a/packages/models/src/eservice/eserviceEvents.ts b/packages/models/src/eservice/eserviceEvents.ts index 7ce9333ffa..0c9758e99c 100644 --- a/packages/models/src/eservice/eserviceEvents.ts +++ b/packages/models/src/eservice/eserviceEvents.ts @@ -41,6 +41,9 @@ import { EServiceRiskAnalysisUpdatedV2, EServiceRiskAnalysisDeletedV2, EServiceDescriptionUpdatedV2, + EServiceDescriptorDelegateSubmittedV2, + EServiceDescriptorDelegatorApprovedV2, + EServiceDescriptorDelegatorRejectedV2, } from "../gen/v2/eservice/events.js"; export function catalogEventToBinaryData(event: EServiceEvent): Uint8Array { @@ -165,6 +168,15 @@ export function catalogEventToBinaryDataV2(event: EServiceEventV2): Uint8Array { .with({ type: "EServiceDescriptionUpdated" }, ({ data }) => EServiceDescriptionUpdatedV2.toBinary(data) ) + .with({ type: "EServiceDescriptorDelegateSubmitted" }, ({ data }) => + EServiceDescriptorDelegateSubmittedV2.toBinary(data) + ) + .with({ type: "EServiceDescriptorDelegatorApproved" }, ({ data }) => + EServiceDescriptorDelegatorApprovedV2.toBinary(data) + ) + .with({ type: "EServiceDescriptorDelegatorRejected" }, ({ data }) => + EServiceDescriptorDelegatorRejectedV2.toBinary(data) + ) .exhaustive(); } @@ -353,6 +365,21 @@ export const EServiceEventV2 = z.discriminatedUnion("type", [ type: z.literal("EServiceDescriptionUpdated"), data: protobufDecoder(EServiceDescriptionUpdatedV2), }), + z.object({ + event_version: z.literal(2), + type: z.literal("EServiceDescriptorDelegateSubmitted"), + data: protobufDecoder(EServiceDescriptorDelegateSubmittedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("EServiceDescriptorDelegatorApproved"), + data: protobufDecoder(EServiceDescriptorDelegatorApprovedV2), + }), + z.object({ + event_version: z.literal(2), + type: z.literal("EServiceDescriptorDelegatorRejected"), + data: protobufDecoder(EServiceDescriptorDelegatorRejectedV2), + }), ]); export type EServiceEventV2 = z.infer; diff --git a/packages/models/src/eservice/protobufConverterFromV2.ts b/packages/models/src/eservice/protobufConverterFromV2.ts index ef891527d6..c7fc18ac6c 100644 --- a/packages/models/src/eservice/protobufConverterFromV2.ts +++ b/packages/models/src/eservice/protobufConverterFromV2.ts @@ -10,6 +10,7 @@ import { EServiceModeV2, EServiceRiskAnalysisV2, EServiceRiskAnalysisFormV2, + DescriptorRejectionReasonV2, } from "../gen/v2/eservice/eservice.js"; import { RiskAnalysis, @@ -29,6 +30,7 @@ import { Descriptor, EService, Document, + DescriptorRejectionReason, } from "./eservice.js"; export const fromAgreementApprovalPolicyV2 = ( @@ -56,6 +58,8 @@ export const fromEServiceDescriptorStateV2 = ( return descriptorState.published; case EServiceDescriptorStateV2.DEPRECATED: return descriptorState.deprecated; + case EServiceDescriptorStateV2.WAITING_FOR_APPROVAL: + return descriptorState.waitingForApproval; } }; @@ -90,6 +94,13 @@ export const fromDocumentV2 = (input: EServiceDocumentV2): Document => ({ uploadDate: new Date(input.uploadDate), }); +export const fromDescriptorRejectionReasonV2 = ( + input: DescriptorRejectionReasonV2 +): DescriptorRejectionReason => ({ + ...input, + rejectedAt: bigIntToDate(input.rejectedAt), +}); + export const fromDescriptorV2 = (input: EServiceDescriptorV2): Descriptor => ({ ...input, id: unsafeBrandId(input.id), @@ -118,6 +129,10 @@ export const fromDescriptorV2 = (input: EServiceDescriptorV2): Descriptor => ({ suspendedAt: bigIntToDate(input.suspendedAt), deprecatedAt: bigIntToDate(input.deprecatedAt), archivedAt: bigIntToDate(input.archivedAt), + rejectionReasons: + input.rejectionReasons.length > 0 + ? input.rejectionReasons.map(fromDescriptorRejectionReasonV2) + : undefined, }); export const fromRiskAnalysisFormV2 = ( diff --git a/packages/models/src/eservice/protobufConverterToV2.ts b/packages/models/src/eservice/protobufConverterToV2.ts index 893ac2c4e9..8e1bed530b 100644 --- a/packages/models/src/eservice/protobufConverterToV2.ts +++ b/packages/models/src/eservice/protobufConverterToV2.ts @@ -1,6 +1,7 @@ import { P, match } from "ts-pattern"; import { AgreementApprovalPolicyV2, + DescriptorRejectionReasonV2, EServiceAttributeV2, EServiceDescriptorStateV2, EServiceDescriptorV2, @@ -15,6 +16,7 @@ import { dateToBigInt } from "../utils.js"; import { AgreementApprovalPolicy, Descriptor, + DescriptorRejectionReason, DescriptorState, Document, EService, @@ -54,6 +56,10 @@ export const toEServiceDescriptorStateV2 = ( descriptorState.deprecated, () => EServiceDescriptorStateV2.DEPRECATED ) + .with( + descriptorState.waitingForApproval, + () => EServiceDescriptorStateV2.WAITING_FOR_APPROVAL + ) .exhaustive(); export const toEServiceTechnologyV2 = ( @@ -79,6 +85,13 @@ export const toEServiceAttributeV2 = ( })), }); +export const toDescriptorRejectedReasonV2 = ( + input: DescriptorRejectionReason +): DescriptorRejectionReasonV2 => ({ + ...input, + rejectedAt: dateToBigInt(input.rejectedAt), +}); + export const toDocumentV2 = (input: Document): EServiceDocumentV2 => ({ ...input, uploadDate: input.uploadDate.toISOString(), @@ -104,6 +117,8 @@ export const toDescriptorV2 = (input: Descriptor): EServiceDescriptorV2 => ({ suspendedAt: dateToBigInt(input.suspendedAt), deprecatedAt: dateToBigInt(input.deprecatedAt), archivedAt: dateToBigInt(input.archivedAt), + rejectionReasons: + input.rejectionReasons?.map(toDescriptorRejectedReasonV2) ?? [], }); export const toRiskAnalysisV2 = ( diff --git a/packages/models/src/tenant/protobufConverterFromV2.ts b/packages/models/src/tenant/protobufConverterFromV2.ts index e61d630bc1..d3baed62c0 100644 --- a/packages/models/src/tenant/protobufConverterFromV2.ts +++ b/packages/models/src/tenant/protobufConverterFromV2.ts @@ -1,6 +1,6 @@ import { createHash } from "crypto"; import { match } from "ts-pattern"; -import { unsafeBrandId } from "../brandedIds.js"; +import { DelegationId, unsafeBrandId } from "../brandedIds.js"; import { genericError } from "../errors.js"; import { TenantKindV2, @@ -84,6 +84,9 @@ export const fromTenantVerifierV2 = ( ): TenantVerifier => ({ ...input, id: unsafeBrandId(input.id), + delegationId: input.delegationId + ? unsafeBrandId(input.delegationId) + : undefined, verificationDate: bigIntToDate(input.verificationDate), expirationDate: bigIntToDate(input.expirationDate), extensionDate: bigIntToDate(input.extensionDate), @@ -92,6 +95,9 @@ export const fromTenantVerifierV2 = ( export const fromTenantRevokerV2 = (input: TenantRevokerV2): TenantRevoker => ({ ...input, id: unsafeBrandId(input.id), + delegationId: input.delegationId + ? unsafeBrandId(input.delegationId) + : undefined, expirationDate: bigIntToDate(input.expirationDate), extensionDate: bigIntToDate(input.extensionDate), revocationDate: bigIntToDate(input.revocationDate), diff --git a/packages/models/src/tenant/protobufConverterToV2.ts b/packages/models/src/tenant/protobufConverterToV2.ts index 36350a983f..fff29dc3d4 100644 --- a/packages/models/src/tenant/protobufConverterToV2.ts +++ b/packages/models/src/tenant/protobufConverterToV2.ts @@ -51,6 +51,7 @@ export function toFeatureV2(feature: TenantFeature): TenantFeatureV2 { export function toTenantVerifierV2(verifier: TenantVerifier): TenantVerifierV2 { return { id: verifier.id, + delegationId: verifier.delegationId, verificationDate: dateToBigInt(verifier.verificationDate), expirationDate: dateToBigInt(verifier.expirationDate), extensionDate: dateToBigInt(verifier.extensionDate), @@ -60,6 +61,7 @@ export function toTenantVerifierV2(verifier: TenantVerifier): TenantVerifierV2 { export function toTenantRevokerV2(revoker: TenantRevoker): TenantRevokerV2 { return { id: revoker.id, + delegationId: revoker.delegationId, verificationDate: dateToBigInt(revoker.verificationDate), expirationDate: dateToBigInt(revoker.expirationDate), extensionDate: dateToBigInt(revoker.extensionDate), diff --git a/packages/models/src/tenant/tenant.ts b/packages/models/src/tenant/tenant.ts index 02f8e93bd7..473c0b13ed 100644 --- a/packages/models/src/tenant/tenant.ts +++ b/packages/models/src/tenant/tenant.ts @@ -1,5 +1,5 @@ import z from "zod"; -import { AttributeId, TenantId } from "../brandedIds.js"; +import { AttributeId, DelegationId, TenantId } from "../brandedIds.js"; export const tenantKind = { PA: "PA", @@ -22,14 +22,26 @@ export const ExternalId = z.object({ export type ExternalId = z.infer; +export const tenantFeatureType = { + persistentCertifier: "PersistentCertifier", + delegatedProducer: "DelegatedProducer", +} as const; + +export const TenantFeatureType = z.enum([ + Object.values(tenantFeatureType)[0], + ...Object.values(tenantFeatureType).slice(1), +]); + +export type TenantFeatureType = z.infer; + export const TenantFeatureCertifier = z.object({ - type: z.literal("PersistentCertifier"), + type: z.literal(tenantFeatureType.persistentCertifier), certifierId: z.string(), }); export type TenantFeatureCertifier = z.infer; export const TenantFeatureDelegatedProducer = z.object({ - type: z.literal("DelegatedProducer"), + type: z.literal(tenantFeatureType.delegatedProducer), availabilityTimestamp: z.coerce.date(), }); export type TenantFeatureDelegatedProducer = z.infer< @@ -58,6 +70,7 @@ export type TenantAttributeType = z.infer; export const TenantVerifier = z.object({ id: TenantId, + delegationId: DelegationId.optional(), verificationDate: z.coerce.date(), expirationDate: z.coerce.date().optional(), extensionDate: z.coerce.date().optional(), @@ -68,6 +81,7 @@ export const TenantRevoker = z.object({ expirationDate: z.coerce.date().optional(), extensionDate: z.coerce.date().optional(), id: TenantId, + delegationId: DelegationId.optional(), revocationDate: z.coerce.date(), verificationDate: z.coerce.date(), }); diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts index fc0da318ca..fa917cd8e5 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts @@ -108,6 +108,9 @@ export const toCatalogItemEventNotification = ( { type: "EServiceDescriptorArchived" }, // CatalogItemDescriptorUpdatedV1 { type: "EServiceDescriptorPublished" }, // CatalogItemDescriptorUpdatedV1 { type: "EServiceDescriptorSuspended" }, // CatalogItemDescriptorUpdatedV1 + { type: "EServiceDescriptorDelegateSubmitted" }, + { type: "EServiceDescriptorDelegatorApproved" }, + { type: "EServiceDescriptorDelegatorRejected" }, { type: "EServiceDescriptorQuotasUpdated" }, (e): CatalogDescriptorNotification => { const catalogItem = getCatalogItem(e); diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts index 0e5ab3a584..894780e623 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMappers.ts @@ -42,6 +42,8 @@ export const toCatalogDescriptorStateV1 = (input: DescriptorState): string => { return "Suspended"; case descriptorState.archived: return "Archived"; + case descriptorState.waitingForApproval: + return "WaitingForApproval"; } }; diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts index cc05355565..4c25629e07 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts @@ -31,6 +31,9 @@ export const eventV2TypeMapper = ( "EServiceDescriptorArchived", "EServiceDescriptorPublished", "EServiceDescriptorSuspended", + "EServiceDescriptorDelegateSubmitted", + "EServiceDescriptorDelegatorApproved", + "EServiceDescriptorDelegatorRejected", () => "catalog_item_descriptor_updated" ) .with( diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 88741fed89..4b61fe333d 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.10", + "@pagopa/interop-outbound-models": "1.0.11a", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} \ No newline at end of file +} diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 35b0bc0497..6b79c90547 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -41,6 +41,7 @@ import { RiskAnalysisId, RiskAnalysis, CorrelationId, + delegationKind, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -105,6 +106,8 @@ import { validateRiskAnalysisOrThrow, assertPurposeTitleIsNotDuplicated, isOverQuota, + assertRequesterIsAllowedToRetrieveRiskAnalysisDocument, + assertRequesterIsProducer, } from "./validators.js"; import { riskAnalysisDocumentBuilder } from "./riskAnalysisDocumentBuilder.js"; @@ -267,11 +270,15 @@ export function purposeServiceBuilder( purpose.data.eserviceId, readModelService ); - getOrganizationRole({ + + await assertRequesterIsAllowedToRetrieveRiskAnalysisDocument({ + eserviceId: eservice.id, organizationId, producerId: eservice.producerId, consumerId: purpose.data.consumerId, + readModelService, }); + const version = retrievePurposeVersion(versionId, purpose); return retrievePurposeVersionDocument(purposeId, version, documentId); @@ -341,9 +348,13 @@ export function purposeServiceBuilder( purpose.data.eserviceId, readModelService ); - if (organizationId !== eservice.producerId) { - throw organizationIsNotTheProducer(organizationId); - } + + await assertRequesterIsProducer({ + eserviceId: eservice.id, + organizationId, + producerId: eservice.producerId, + readModelService, + }); const purposeVersion = retrievePurposeVersion(versionId, purpose); @@ -535,10 +546,12 @@ export function purposeServiceBuilder( readModelService ); - const suspender = getOrganizationRole({ + const suspender = await getOrganizationRole({ + eserviceId: eservice.id, organizationId, producerId: eservice.producerId, consumerId: purpose.data.consumerId, + readModelService, }); const suspendedPurposeVersion: PurposeVersion = { @@ -812,10 +825,12 @@ export function purposeServiceBuilder( }); } - const purposeOwnership = getOrganizationRole({ + const purposeOwnership = await getOrganizationRole({ + eserviceId: eservice.id, organizationId, producerId: eservice.producerId, consumerId: purpose.data.consumerId, + readModelService, }); const { event, updatedPurposeVersion } = await match({ @@ -1331,24 +1346,39 @@ const authorizeRiskAnalysisForm = ({ } }; -const getOrganizationRole = ({ +const getOrganizationRole = async ({ + eserviceId, organizationId, producerId, consumerId, + readModelService, }: { + eserviceId: EServiceId; organizationId: TenantId; producerId: TenantId; consumerId: TenantId; -}): Ownership => { + readModelService: ReadModelService; +}): Promise => { if (producerId === consumerId && organizationId === producerId) { return ownership.SELF_CONSUMER; } else if (producerId !== consumerId && organizationId === consumerId) { return ownership.CONSUMER; - } else if (producerId !== consumerId && organizationId === producerId) { + } + + const activeProducerDelegation = await readModelService.getActiveDelegation( + eserviceId, + delegationKind.delegatedProducer + ); + + if ( + (activeProducerDelegation && + organizationId === activeProducerDelegation.delegateId) || + (!activeProducerDelegation && organizationId === producerId) + ) { return ownership.PRODUCER; - } else { - throw organizationNotAllowed(organizationId); } + + throw organizationNotAllowed(organizationId); }; const replacePurposeVersion = ( diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 1fd4bb75bf..2b17ecda7c 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -23,6 +23,9 @@ import { agreementState, PurposeVersionState, TenantReadModel, + delegationState, + Delegation, + DelegationKind, } from "pagopa-interop-models"; import { Document, Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -233,7 +236,8 @@ async function buildGetPurposesAggregation( export function readModelServiceBuilder( readModelRepository: ReadModelRepository ) { - const { eservices, purposes, tenants, agreements } = readModelRepository; + const { eservices, purposes, tenants, agreements, delegations } = + readModelRepository; return { async getEServiceById(id: EServiceId): Promise { @@ -333,6 +337,25 @@ export function readModelServiceBuilder( return result.data; }, + async getActiveDelegation( + eserviceId: EServiceId, + kind: DelegationKind + ): Promise { + const data = await delegations.findOne({ + "data.eserviceId": eserviceId, + "data.kind": kind, + "data.state": delegationState.active, + }); + if (!data) { + return undefined; + } else { + const result = Delegation.safeParse(data.data); + if (!result.success) { + throw genericError("Unable to parse delegation item"); + } + return result.data; + } + }, }; } diff --git a/packages/purpose-process/src/services/validators.ts b/packages/purpose-process/src/services/validators.ts index bee99fba71..22a682c858 100644 --- a/packages/purpose-process/src/services/validators.ts +++ b/packages/purpose-process/src/services/validators.ts @@ -9,6 +9,7 @@ import { TenantKind, purposeVersionState, EServiceId, + delegationKind, } from "pagopa-interop-models"; import { validateRiskAnalysis, @@ -23,6 +24,8 @@ import { eServiceModeNotAllowed, missingFreeOfChargeReason, organizationIsNotTheConsumer, + organizationIsNotTheProducer, + organizationNotAllowed, purposeNotInDraftState, riskAnalysisValidationFailed, } from "../model/domain/errors.js"; @@ -248,3 +251,62 @@ export async function isOverQuota( allPurposesRequestsSum + dailyCalls <= maxDailyCallsTotal ); } + +export const assertRequesterIsAllowedToRetrieveRiskAnalysisDocument = async ({ + eserviceId, + organizationId, + producerId, + consumerId, + readModelService, +}: { + eserviceId: EServiceId; + organizationId: TenantId; + producerId: TenantId; + consumerId: TenantId; + readModelService: ReadModelService; +}): Promise => { + if (organizationId === producerId || organizationId === consumerId) { + return; + } + + const activeProducerDelegation = await readModelService.getActiveDelegation( + eserviceId, + delegationKind.delegatedProducer + ); + + if ( + activeProducerDelegation && + organizationId === activeProducerDelegation.delegateId + ) { + return; + } + + throw organizationNotAllowed(organizationId); +}; + +export const assertRequesterIsProducer = async ({ + eserviceId, + organizationId, + producerId, + readModelService, +}: { + eserviceId: EServiceId; + organizationId: TenantId; + producerId: TenantId; + readModelService: ReadModelService; +}): Promise => { + const activeProducerDelegation = await readModelService.getActiveDelegation( + eserviceId, + delegationKind.delegatedProducer + ); + + if ( + (activeProducerDelegation && + organizationId === activeProducerDelegation.delegateId) || + (!activeProducerDelegation && organizationId === producerId) + ) { + return; + } + + throw organizationIsNotTheProducer(organizationId); +}; diff --git a/packages/purpose-process/test/activatePurposeVersion.test.ts b/packages/purpose-process/test/activatePurposeVersion.test.ts index d3001e45b4..c24c7cd809 100644 --- a/packages/purpose-process/test/activatePurposeVersion.test.ts +++ b/packages/purpose-process/test/activatePurposeVersion.test.ts @@ -10,9 +10,10 @@ import { getMockEService, getMockAgreement, getMockValidRiskAnalysisForm, - writeInReadmodel, readLastEventByStreamId, decodeProtobufPayload, + getMockDelegation, + getMockAuthData, } from "pagopa-interop-commons-test"; import { PurposeVersion, @@ -24,7 +25,6 @@ import { Agreement, Descriptor, agreementState, - toReadModelEService, TenantKind, PurposeActivatedV2, toPurposeV2, @@ -33,9 +33,9 @@ import { PurposeVersionOverQuotaUnsuspendedV2, PurposeWaitingForApprovalV2, eserviceMode, - toReadModelAgreement, PurposeVersionActivatedV2, - toReadModelTenant, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { @@ -55,11 +55,12 @@ import { agreementNotFound, } from "../src/model/domain/errors.js"; import { - agreements, - eservices, + addOneAgreement, + addOneDelegation, + addOneEService, + addOneTenant, postgresDB, purposeService, - tenants, } from "./utils.js"; import { addOnePurpose } from "./utils.js"; @@ -125,10 +126,10 @@ describe("activatePurposeVersion", () => { it("should write on event-store for the activation of a purpose version in the waiting for approval state", async () => { await addOnePurpose(mockPurpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -181,10 +182,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -237,10 +238,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -294,10 +295,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -360,10 +361,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -415,10 +416,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -466,10 +467,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const purposeVersion = await purposeService.activatePurposeVersion({ purposeId: mockPurpose.id, @@ -514,10 +515,10 @@ describe("activatePurposeVersion", () => { const purpose: Purpose = { ...mockPurpose, versions: [purposeVersion] }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -538,10 +539,10 @@ describe("activatePurposeVersion", () => { const purpose: Purpose = { ...mockPurpose, versions: [purposeVersion] }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -572,10 +573,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(eservice), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(consumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(eservice); + await addOneAgreement(mockAgreement); + await addOneTenant(consumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -600,10 +601,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -618,9 +619,9 @@ describe("activatePurposeVersion", () => { it("should throw eserviceNotFound if the e-service does not exists in the readmodel", async () => { await addOnePurpose(mockPurpose); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -641,9 +642,9 @@ describe("activatePurposeVersion", () => { const purpose: Purpose = { ...mockPurpose, versions: [purposeVersion] }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -674,10 +675,10 @@ describe("activatePurposeVersion", () => { const purpose: Purpose = { ...mockPurpose, versions: [purposeVersion] }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(agreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(agreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -693,15 +694,15 @@ describe("activatePurposeVersion", () => { } ); - it("should throw organizationNotAllowed if the caller is neither the producer or the consumer of the purpose", async () => { + it("should throw organizationNotAllowed if the caller is neither the producer or the consumer of the purpose, nor the delegate", async () => { const anotherTenant: Tenant = { ...getMockTenant(), kind: "PA" }; await addOnePurpose(mockPurpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); - await writeInReadmodel(toReadModelTenant(anotherTenant), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); + await addOneTenant(anotherTenant); expect(async () => { await purposeService.activatePurposeVersion({ @@ -714,6 +715,67 @@ describe("activatePurposeVersion", () => { }).rejects.toThrowError(organizationNotAllowed(anotherTenant.id)); }); + it("should throw organizationNotAllowed if the caller is the producer but the purpose e-service has an active delegation", async () => { + await addOnePurpose(mockPurpose); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await addOneDelegation(delegation); + + expect(async () => { + await purposeService.activatePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockProducer.id, + correlationId: generateId(), + logger: genericLogger, + }); + }).rejects.toThrowError(organizationNotAllowed(mockProducer.id)); + }); + + it.each( + Object.values(delegationState).filter((s) => s !== delegationState.active) + )( + "should throw organizationNotAllowed if the caller is the purpose e-service delegate but the delegation is in %s state", + async (delegationState) => { + await addOnePurpose(mockPurpose); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState, + }); + + await addOneDelegation(delegation); + + expect(async () => { + await purposeService.activatePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: delegate.organizationId, + correlationId: generateId(), + logger: genericLogger, + }); + }).rejects.toThrowError(organizationNotAllowed(delegate.organizationId)); + } + ); + it("should throw missingRiskAnalysis if the purpose is in draft and has no risk analysis", async () => { const purposeVersion: PurposeVersion = { ...mockPurposeVersion, @@ -726,10 +788,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -756,10 +818,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); const result = validateRiskAnalysis( riskAnalysisFormToRiskAnalysisFormToValidate(riskAnalysisForm), @@ -793,9 +855,9 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -810,9 +872,9 @@ describe("activatePurposeVersion", () => { it("should throw tenantNotFound if the purpose producer is not found in the readmodel", async () => { await addOnePurpose(mockPurpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -833,10 +895,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(mockPurpose); - await writeInReadmodel(toReadModelEService(eservice), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(consumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(eservice); + await addOneAgreement(mockAgreement); + await addOneTenant(consumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -857,10 +919,10 @@ describe("activatePurposeVersion", () => { }; await addOnePurpose(mockPurpose); - await writeInReadmodel(toReadModelEService(eservice), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(producer), tenants); + await addOneEService(eservice); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(producer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -887,10 +949,10 @@ describe("activatePurposeVersion", () => { const purpose: Purpose = { ...mockPurpose, versions: [purposeVersion] }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ @@ -918,10 +980,10 @@ describe("activatePurposeVersion", () => { const purpose: Purpose = { ...mockPurpose, versions: [purposeVersion] }; await addOnePurpose(purpose); - await writeInReadmodel(toReadModelEService(mockEService), eservices); - await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); expect(async () => { await purposeService.activatePurposeVersion({ diff --git a/packages/purpose-process/test/getRiskAnalysisDocument.test.ts b/packages/purpose-process/test/getRiskAnalysisDocument.test.ts index 4d2ff798a8..49c94cc3ed 100644 --- a/packages/purpose-process/test/getRiskAnalysisDocument.test.ts +++ b/packages/purpose-process/test/getRiskAnalysisDocument.test.ts @@ -4,6 +4,8 @@ import { getMockPurposeVersion, getMockPurpose, writeInReadmodel, + getMockAuthData, + getMockDelegation, } from "pagopa-interop-commons-test"; import { Purpose, @@ -13,6 +15,8 @@ import { PurposeVersionId, PurposeVersionDocumentId, TenantId, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; @@ -28,28 +32,51 @@ import { addOnePurpose, eservices, purposeService, + delegations, } from "./utils.js"; describe("getRiskAnalysisDocument", () => { - it("should get the purpose version document", async () => { + it("should get the purpose version document (consumer)", async () => { const mockDocument = getMockPurposeVersionDocument(); const mockEService = getMockEService(); const mockPurposeVersion = { ...getMockPurposeVersion(), riskAnalysis: mockDocument, }; - const mockPurpose1: Purpose = { + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const result = await purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: mockPurpose.consumerId, + logger: genericLogger, + }); + expect(result).toEqual(mockDocument); + }); + it("should get the purpose version document (producer)", async () => { + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { ...getMockPurpose(), eserviceId: mockEService.id, versions: [mockPurposeVersion], }; - const mockPurpose2 = getMockPurpose(); - await addOnePurpose(mockPurpose1); - await addOnePurpose(mockPurpose2); + await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); const result = await purposeService.getRiskAnalysisDocument({ - purposeId: mockPurpose1.id, + purposeId: mockPurpose.id, versionId: mockPurposeVersion.id, documentId: mockDocument.id, organizationId: mockEService.producerId, @@ -57,6 +84,40 @@ describe("getRiskAnalysisDocument", () => { }); expect(result).toEqual(mockDocument); }); + it("should get the purpose version document (delegate)", async () => { + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + const result = await purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: delegate.organizationId, + logger: genericLogger, + }); + expect(result).toEqual(mockDocument); + }); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const notExistingId: PurposeId = generateId(); await addOnePurpose(getMockPurpose()); @@ -134,7 +195,7 @@ describe("getRiskAnalysisDocument", () => { ) ); }); - it("should throw organizationNotAllowed if the requester is not the producer nor the consumer", async () => { + it("should throw organizationNotAllowed if the requester is not the producer nor the consumer nor the delegate", async () => { const randomId: TenantId = generateId(); const mockDocument = getMockPurposeVersionDocument(); const mockEService = getMockEService(); @@ -161,4 +222,45 @@ describe("getRiskAnalysisDocument", () => { }) ).rejects.toThrowError(organizationNotAllowed(randomId)); }); + it.each( + Object.values(delegationState).filter((s) => s !== delegationState.active) + )( + "should throw organizationNotAllowed if the requester is the delegate but the delegation is in %s state", + async (delegationState) => { + const mockDocument = getMockPurposeVersionDocument(); + const mockEService = getMockEService(); + const mockPurposeVersion = { + ...getMockPurposeVersion(), + riskAnalysis: mockDocument, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState, + }); + + await writeInReadmodel(delegation, delegations); + + expect( + purposeService.getRiskAnalysisDocument({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + documentId: mockDocument.id, + organizationId: delegate.organizationId, + logger: genericLogger, + }) + ).rejects.toThrowError(organizationNotAllowed(delegate.organizationId)); + } + ); }); diff --git a/packages/purpose-process/test/rejectPurposeVersion.test.ts b/packages/purpose-process/test/rejectPurposeVersion.test.ts index 3ab8cae276..5312c58641 100644 --- a/packages/purpose-process/test/rejectPurposeVersion.test.ts +++ b/packages/purpose-process/test/rejectPurposeVersion.test.ts @@ -4,6 +4,8 @@ import { getMockPurpose, writeInReadmodel, decodeProtobufPayload, + getMockAuthData, + getMockDelegation, } from "pagopa-interop-commons-test"; import { purposeVersionState, @@ -15,6 +17,8 @@ import { toPurposeV2, PurposeId, PurposeVersionId, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { describe, expect, it, vi } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; @@ -31,10 +35,11 @@ import { readLastPurposeEvent, eservices, purposeService, + delegations, } from "./utils.js"; 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()); @@ -91,6 +96,73 @@ describe("rejectPurposeVersion", () => { vi.useRealTimers(); }); + it("should write on event-store for the rejection of a purpose version when the requester is delegate", 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); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + await purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: delegate.organizationId, + correlationId: generateId(), + logger: genericLogger, + }); + + const writtenEvent = await readLastPurposeEvent(mockPurpose.id); + + 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(); @@ -137,7 +209,7 @@ describe("rejectPurposeVersion", () => { }) ).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 nor delegate", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); const mockPurpose: Purpose = { @@ -162,6 +234,118 @@ describe("rejectPurposeVersion", () => { organizationIsNotTheProducer(mockPurpose.consumerId) ); }); + it("should throw organizationIsNotTheProducer if the purpose e-service has an active delegation and the requester is the producer", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: mockEService.producerId, + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError( + organizationIsNotTheProducer(mockEService.producerId) + ); + }); + it("should throw organizationIsNotTheProducer if the purpose e-service has an active delegation and the requester is not the producer nor the delegate", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + const randomCaller = getMockAuthData(); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: randomCaller.organizationId, + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError( + organizationIsNotTheProducer(randomCaller.organizationId) + ); + }); + it.each( + Object.values(delegationState).filter((s) => s !== delegationState.active) + )( + "should throw organizationIsNotTheProducer if the requester is the e-service delegate but the delegation is in %s state", + async (delegationState) => { + const mockEService = getMockEService(); + const mockPurposeVersion = getMockPurposeVersion(); + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState, + }); + + await writeInReadmodel(delegation, delegations); + + expect( + purposeService.rejectPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + rejectionReason: "test", + organizationId: delegate.organizationId, + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError( + organizationIsNotTheProducer(delegate.organizationId) + ); + } + ); it("should throw purposeVersionNotFound if the purpose version doesn't exist", async () => { const mockEService = getMockEService(); const mockPurposeVersion = getMockPurposeVersion(); diff --git a/packages/purpose-process/test/suspendPurposeVersion.test.ts b/packages/purpose-process/test/suspendPurposeVersion.test.ts index 343cfcdf2d..b0f715932a 100644 --- a/packages/purpose-process/test/suspendPurposeVersion.test.ts +++ b/packages/purpose-process/test/suspendPurposeVersion.test.ts @@ -5,6 +5,8 @@ import { getMockPurpose, writeInReadmodel, decodeProtobufPayload, + getMockAuthData, + getMockDelegation, } from "pagopa-interop-commons-test"; import { PurposeVersion, @@ -19,6 +21,8 @@ import { PurposeVersionId, TenantId, toPurposeVersionV2, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { genericLogger } from "pagopa-interop-commons"; import { @@ -29,6 +33,7 @@ import { } from "../src/model/domain/errors.js"; import { addOnePurpose, + delegations, eservices, getMockEService, purposeService, @@ -160,6 +165,78 @@ describe("suspendPurposeVersion", () => { vi.useRealTimers(); }); + it("should write on event-store for the suspension of a purpose version by the delegated 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); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + const returnedPurposeVersion = await purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion1.id, + organizationId: delegate.organizationId, + correlationId: generateId(), + logger: genericLogger, + }); + + const writtenEvent = await readLastPurposeEvent(mockPurpose.id); + + 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)); + expect( + writtenPayload.purpose?.versions.find( + (v) => v.id === returnedPurposeVersion.id + ) + ).toEqual(toPurposeVersionV2(returnedPurposeVersion)); + + 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()); @@ -290,6 +367,115 @@ describe("suspendPurposeVersion", () => { }) ).rejects.toThrowError(organizationNotAllowed(randomId)); }); + it("should throw organizationNotAllowed if the requester is not the e-service active delegation delegate", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + const randomCaller = getMockAuthData(); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: randomCaller.organizationId, + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError(organizationNotAllowed(randomCaller.organizationId)); + }); + it.each( + Object.values(delegationState).filter((s) => s !== delegationState.active) + )( + "should throw organizationNotAllowed if the requester is the e-service delegate but the delegation is in %s state", + async (delegationState) => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState, + }); + + await writeInReadmodel(delegation, delegations); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: delegate.organizationId, + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError(organizationNotAllowed(delegate.organizationId)); + } + ); + it("should throw organizationNotAllowed if the requester is the producer but the purpose e-service has an active delegation", async () => { + const mockEService = getMockEService(); + const mockPurposeVersion: PurposeVersion = { + ...getMockPurposeVersion(), + state: purposeVersionState.active, + }; + const mockPurpose: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + versions: [mockPurposeVersion], + }; + await addOnePurpose(mockPurpose); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + + const delegate = getMockAuthData(); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + delegateId: delegate.organizationId, + state: delegationState.active, + }); + + await writeInReadmodel(delegation, delegations); + + expect( + purposeService.suspendPurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: mockEService.producerId, + correlationId: generateId(), + logger: genericLogger, + }) + ).rejects.toThrowError(organizationNotAllowed(mockEService.producerId)); + }); it.each( Object.values(purposeVersionState).filter( (state) => diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index fcdd9e28fe..7d67e41b51 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -24,6 +24,12 @@ import { unsafeBrandId, toReadModelPurpose, PurposeId, + toReadModelEService, + Tenant, + toReadModelTenant, + toReadModelAgreement, + Agreement, + Delegation, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { afterAll, afterEach, inject, vi } from "vitest"; @@ -46,6 +52,7 @@ export const eservices = readModelRepository.eservices; export const tenants = readModelRepository.tenants; export const attributes = readModelRepository.attributes; export const purposes = readModelRepository.purposes; +export const delegations = readModelRepository.delegations; export const readModelService = readModelServiceBuilder(readModelRepository); @@ -74,6 +81,24 @@ export const addOnePurpose = async (purpose: Purpose): Promise => { await writeInReadmodel(toReadModelPurpose(purpose), purposes); }; +export const addOneEService = async (eservice: EService): Promise => { + await writeInReadmodel(toReadModelEService(eservice), eservices); +}; + +export const addOneTenant = async (tenant: Tenant): Promise => { + await writeInReadmodel(toReadModelTenant(tenant), tenants); +}; + +export const addOneAgreement = async (agreement: Agreement): Promise => { + await writeInReadmodel(toReadModelAgreement(agreement), agreements); +}; + +export const addOneDelegation = async ( + delegation: Delegation +): Promise => { + await writeInReadmodel(delegation, delegations); +}; + export const writePurposeInEventstore = async ( purpose: Purpose ): Promise => { diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index e7577c6274..02f287a2c1 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.10", + "@pagopa/interop-outbound-models": "1.0.11a", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", @@ -40,4 +40,4 @@ "ts-pattern": "5.2.0", "zod": "3.23.8" } -} \ No newline at end of file +} diff --git a/packages/tenant-process/src/model/domain/apiConverter.ts b/packages/tenant-process/src/model/domain/apiConverter.ts index 77db01d68b..40363dab79 100644 --- a/packages/tenant-process/src/model/domain/apiConverter.ts +++ b/packages/tenant-process/src/model/domain/apiConverter.ts @@ -11,6 +11,8 @@ import { tenantMailKind, TenantFeature, tenantAttributeType, + TenantFeatureType, + tenantFeatureType, } from "pagopa-interop-models"; import { tenantApi } from "pagopa-interop-api-clients"; import { match } from "ts-pattern"; @@ -56,6 +58,7 @@ export function toApiTenantVerifier( verificationDate: verifier.verificationDate.toJSON(), expirationDate: verifier.expirationDate?.toJSON(), extensionDate: verifier.extensionDate?.toJSON(), + delegationId: verifier.delegationId, }; } @@ -68,6 +71,7 @@ export function toApiTenantRevoker( expirationDate: revoker.expirationDate?.toJSON(), extensionDate: revoker.extensionDate?.toJSON(), revocationDate: revoker.revocationDate.toJSON(), + delegationId: revoker.delegationId, }; } @@ -133,3 +137,18 @@ export function toApiTenant(tenant: Tenant): tenantApi.Tenant { subUnitType: tenant.subUnitType, }; } + +export function apiTenantFeatureTypeToTenantFeatureType( + input: tenantApi.TenantFeatureType +): TenantFeatureType { + return match(input) + .with( + tenantApi.TenantFeatureType.Values.DELEGATED_PRODUCER, + () => tenantFeatureType.delegatedProducer + ) + .with( + tenantApi.TenantFeatureType.Values.PERSISTENT_CERTIFIER, + () => tenantFeatureType.persistentCertifier + ) + .exhaustive(); +} diff --git a/packages/tenant-process/src/model/domain/errors.ts b/packages/tenant-process/src/model/domain/errors.ts index 6b9e96f400..7f1342f7f5 100644 --- a/packages/tenant-process/src/model/domain/errors.ts +++ b/packages/tenant-process/src/model/domain/errors.ts @@ -36,6 +36,8 @@ export const errorCodes = { tenantAlreadyHasDelegatedProducerFeature: "0027", tenantHasNoDelegatedProducerFeature: "0028", notValidMailAddress: "0029", + agreementNotFound: "0030", + descriptorNotFoundInEservice: "0031", }; export type ErrorCodes = keyof typeof errorCodes; @@ -316,6 +318,25 @@ export function tenantHasNoDelegatedProducerFeature( }); } +export function agreementNotFound(agreementId: string): ApiError { + return new ApiError({ + detail: `Agreement ${agreementId} not found`, + code: "agreementNotFound", + title: "Agreement not found", + }); +} + +export function descriptorNotFoundInEservice( + descriptorId: string, + eserviceId: string +): ApiError { + return new ApiError({ + detail: `Descriptor ${descriptorId} not found in EService ${eserviceId}`, + code: "descriptorNotFoundInEservice", + title: "Descriptor not found in EService", + }); +} + export function notValidMailAddress(address: string): ApiError { return new ApiError({ detail: `mail address ${address} not valid`, diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index 2416c5457d..c400741088 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -12,7 +12,10 @@ import { } from "pagopa-interop-commons"; import { unsafeBrandId } from "pagopa-interop-models"; import { tenantApi } from "pagopa-interop-api-clients"; -import { toApiTenant } from "../model/domain/apiConverter.js"; +import { + apiTenantFeatureTypeToTenantFeatureType, + toApiTenant, +} from "../model/domain/apiConverter.js"; import { makeApiProblem } from "../model/domain/errors.js"; import { getTenantByExternalIdErrorMapper, @@ -168,10 +171,11 @@ const tenantsRouter = ( const ctx = fromAppContext(req.ctx); try { - const { name, offset, limit } = req.query; - const tenants = await tenantService.getTenantsByName( + const { name, features, offset, limit } = req.query; + const tenants = await tenantService.getTenants( { name, + features: features.map(apiTenantFeatureTypeToTenantFeatureType), offset, limit, }, @@ -852,6 +856,7 @@ const tenantsRouter = ( { tenantId: unsafeBrandId(req.params.tenantId), attributeId: unsafeBrandId(req.params.attributeId), + agreementId: unsafeBrandId(req.body.agreementId), }, ctx ); diff --git a/packages/tenant-process/src/services/readModelService.ts b/packages/tenant-process/src/services/readModelService.ts index 27478f3fe1..19caae5843 100644 --- a/packages/tenant-process/src/services/readModelService.ts +++ b/packages/tenant-process/src/services/readModelService.ts @@ -20,13 +20,18 @@ import { AgreementState, TenantReadModel, genericInternalError, + TenantFeatureType, + AgreementId, + DelegationKind, + Delegation, } from "pagopa-interop-models"; import { tenantApi } from "pagopa-interop-api-clients"; import { z } from "zod"; import { Document, Filter, WithId } from "mongodb"; function listTenantsFilters( - name: string | undefined + name: string | undefined, + features?: TenantFeatureType[] ): Filter<{ data: TenantReadModel }> { const nameFilter = name ? { @@ -37,6 +42,15 @@ function listTenantsFilters( } : {}; + const featuresFilter = + features && features.length > 0 + ? { + "data.features.type": { + $in: features, + }, + } + : {}; + const withSelfcareIdFilter = { "data.selfcareId": { $exists: true, @@ -45,6 +59,7 @@ function listTenantsFilters( return { ...nameFilter, + ...featuresFilter, ...withSelfcareIdFilter, }; } @@ -145,18 +160,21 @@ async function getTenant( export function readModelServiceBuilder( readModelRepository: ReadModelRepository ) { - const { attributes, eservices, tenants, agreements } = readModelRepository; + const { attributes, eservices, tenants, agreements, delegations } = + readModelRepository; return { - async getTenantsByName({ + async getTenants({ name, + features, offset, limit, }: { name: string | undefined; + features: TenantFeatureType[]; offset: number; limit: number; }): Promise> { - const query = listTenantsFilters(name); + const query = listTenantsFilters(name, features); const aggregationPipeline = [ { $match: query }, { $project: { data: 1, lowerName: { $toLower: "$data.name" } } }, @@ -394,6 +412,24 @@ export function readModelServiceBuilder( return result.data; }, + async getAgreementById( + agreementId: AgreementId + ): Promise { + const data = await agreements.findOne({ "data.id": agreementId }); + if (data) { + const result = Agreement.safeParse(data.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse agreement item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return result.data; + } + return undefined; + }, + async getCertifiedAttributes({ certifierId, offset, @@ -496,6 +532,33 @@ export function readModelServiceBuilder( "data.origin": certifierId, }); }, + + async getActiveDelegation({ + eserviceId, + kind, + }: { + eserviceId: EServiceId; + kind: DelegationKind; + }): Promise { + const data = await delegations.findOne({ + "data.eserviceId": eserviceId, + "data.kind": kind, + "data.state": agreementState.active, + }); + + if (data) { + const result = Delegation.safeParse(data.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return result.data; + } + return undefined; + }, }; } diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index 647ab5c2d3..3536a59928 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -32,6 +32,15 @@ import { TenantFeatureCertifier, CorrelationId, tenantKind, + TenantFeatureType, + AgreementId, + Agreement, + delegationKind, + Delegation, + DelegationKind, + AgreementState, + EServiceId, + DelegationId, } from "pagopa-interop-models"; import { ExternalId } from "pagopa-interop-models"; import { tenantApi } from "pagopa-interop-api-clients"; @@ -72,6 +81,7 @@ import { tenantNotFound, tenantIsAlreadyACertifier, verifiedAttributeSelfRevocationNotAllowed, + agreementNotFound, notValidMailAddress, } from "../model/domain/errors.js"; import { @@ -154,6 +164,33 @@ async function retrieveCertifiedAttribute({ return attribute; } +async function retrieveAgreement( + agreementId: AgreementId, + readModelService: ReadModelService +): Promise { + const agreement = await readModelService.getAgreementById(agreementId); + if (!agreement) { + throw agreementNotFound(agreementId); + } + return agreement; +} + +async function retrieveActiveDelegation( + { + eserviceId, + kind, + }: { + eserviceId: EServiceId; + kind: DelegationKind; + }, + readModelService: ReadModelService +): Promise { + return await readModelService.getActiveDelegation({ + eserviceId, + kind, + }); +} + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tenantServiceBuilder( dbInstance: DB, @@ -675,23 +712,46 @@ export function tenantServiceBuilder( logger: Logger ): Promise { logger.info( - `Verifying attribute ${tenantAttributeSeed.id} to tenant ${tenantId}` + `Verifying attribute ${tenantAttributeSeed.id} to tenant ${tenantId} for agreement ${tenantAttributeSeed.agreementId}` ); const attributeId = unsafeBrandId(tenantAttributeSeed.id); + const agreementId = unsafeBrandId( + tenantAttributeSeed.agreementId + ); + + const agreement = await retrieveAgreement(agreementId, readModelService); - const allowedStatuses = [ + const error = attributeVerificationNotAllowed(tenantId, attributeId); + + const allowedStatuses: AgreementState[] = [ agreementState.pending, agreementState.active, agreementState.suspended, ]; + if (!allowedStatuses.includes(agreement.state)) { + throw error; + } + + const producerDelegation = await retrieveActiveDelegation( + { + eserviceId: agreement.eserviceId, + kind: delegationKind.delegatedProducer, + }, + readModelService + ); + + const delegateProducerId = producerDelegation?.delegateId; + const producerDelegator = producerDelegation?.delegatorId; + await assertVerifiedAttributeOperationAllowed({ - producerId: organizationId, + requesterId: organizationId, + delegateProducerId, consumerId: tenantId, attributeId, - agreementStates: allowedStatuses, + agreement, readModelService, - error: attributeVerificationNotAllowed(tenantId, attributeId), + error, }); const targetTenant = await retrieveTenant(tenantId, readModelService); @@ -713,12 +773,14 @@ export function tenantServiceBuilder( ? reassignVerifiedAttribute( targetTenant.data.attributes, verifiedTenantAttribute, - organizationId, + producerDelegator ?? organizationId, + producerDelegation?.id, tenantAttributeSeed ) : assignVerifiedAttribute( targetTenant.data.attributes, - organizationId, + producerDelegator ?? organizationId, + producerDelegation?.id, tenantAttributeSeed ), @@ -740,9 +802,11 @@ export function tenantServiceBuilder( { tenantId, attributeId, + agreementId, }: { tenantId: TenantId; attributeId: AttributeId; + agreementId: AgreementId; }, { logger, authData, correlationId }: WithLogger ): Promise { @@ -754,23 +818,41 @@ export function tenantServiceBuilder( throw verifiedAttributeSelfRevocationNotAllowed(); } - const allowedStatuses = [ + const targetTenant = await retrieveTenant(tenantId, readModelService); + const agreement = await retrieveAgreement(agreementId, readModelService); + + const error = attributeRevocationNotAllowed(tenantId, attributeId); + + const allowedStatuses: AgreementState[] = [ agreementState.pending, agreementState.active, agreementState.suspended, ]; + if (!allowedStatuses.includes(agreement.state)) { + throw error; + } + + const producerDelegation = await retrieveActiveDelegation( + { + eserviceId: agreement.eserviceId, + kind: delegationKind.delegatedProducer, + }, + readModelService + ); + + const delegateProducerId = producerDelegation?.delegateId; + const producerDelegator = producerDelegation?.delegatorId; await assertVerifiedAttributeOperationAllowed({ - producerId: authData.organizationId, + requesterId: authData.organizationId, + delegateProducerId, consumerId: tenantId, attributeId, - agreementStates: allowedStatuses, + agreement, readModelService, - error: attributeRevocationNotAllowed(tenantId, attributeId), + error, }); - const targetTenant = await retrieveTenant(tenantId, readModelService); - const verifiedTenantAttribute = targetTenant.data.attributes.find( (attr): attr is VerifiedTenantAttribute => attr.type === tenantAttributeType.VERIFIED && attr.id === attributeId @@ -785,7 +867,7 @@ export function tenantServiceBuilder( ); if (!verifier) { - throw attributeRevocationNotAllowed(tenantId, attributeId); + throw error; } const isInRevokedBy = verifiedTenantAttribute.revokedBy.some( @@ -814,6 +896,8 @@ export function tenantServiceBuilder( ...verifiedTenantAttribute.revokedBy, { ...verifier, + id: producerDelegator ?? verifier.id, + delegationId: producerDelegation?.id, revocationDate: new Date(), }, ], @@ -1209,22 +1293,24 @@ export function tenantServiceBuilder( limit, }); }, - async getTenantsByName( + async getTenants( { name, + features, offset, limit, }: { name: string | undefined; + features: TenantFeatureType[]; offset: number; limit: number; }, logger: Logger ): Promise> { logger.info( - `Retrieving Tenants with name = ${name}, limit = ${limit}, offset = ${offset}` + `Retrieving Tenants with name = ${name}, features = ${features}, limit = ${limit}, offset = ${offset}` ); - return readModelService.getTenantsByName({ name, offset, limit }); + return readModelService.getTenants({ name, features, offset, limit }); }, async getTenantById(id: TenantId, logger: Logger): Promise { logger.info(`Retrieving tenant ${id}`); @@ -1753,6 +1839,7 @@ function assignCertifiedAttribute({ function buildVerifiedBy( verifiers: TenantVerifier[], organizationId: TenantId, + producerDelegation: DelegationId | undefined, expirationDate: string | undefined ): TenantVerifier[] { const hasPreviouslyVerified = verifiers.find((i) => i.id === organizationId); @@ -1761,6 +1848,7 @@ function buildVerifiedBy( verification.id === organizationId ? { id: organizationId, + delegationId: producerDelegation, verificationDate: new Date(), expirationDate: expirationDate ? new Date(expirationDate) @@ -1775,6 +1863,7 @@ function buildVerifiedBy( ...verifiers, { id: organizationId, + delegationId: producerDelegation, verificationDate: new Date(), expirationDate: expirationDate ? new Date(expirationDate) : undefined, extensionDate: expirationDate ? new Date(expirationDate) : undefined, @@ -1815,6 +1904,7 @@ function reassignDeclaredAttribute( function assignVerifiedAttribute( attributes: TenantAttribute[], organizationId: TenantId, + producerDelegationId: DelegationId | undefined, tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed ): TenantAttribute[] { return [ @@ -1826,6 +1916,7 @@ function assignVerifiedAttribute( verifiedBy: [ { id: organizationId, + delegationId: producerDelegationId, verificationDate: new Date(), expirationDate: tenantAttributeSeed.expirationDate ? new Date(tenantAttributeSeed.expirationDate) @@ -1844,6 +1935,7 @@ function reassignVerifiedAttribute( attributes: TenantAttribute[], verifiedTenantAttribute: VerifiedTenantAttribute, organizationId: TenantId, + producerDelegationId: DelegationId | undefined, tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed ): TenantAttribute[] { return attributes.map((attr) => @@ -1853,6 +1945,7 @@ function reassignVerifiedAttribute( verifiedBy: buildVerifiedBy( verifiedTenantAttribute.verifiedBy, organizationId, + producerDelegationId, tenantAttributeSeed.expirationDate ), revokedBy: verifiedTenantAttribute.revokedBy.filter( diff --git a/packages/tenant-process/src/services/validators.ts b/packages/tenant-process/src/services/validators.ts index 706d86e79f..11772337e6 100644 --- a/packages/tenant-process/src/services/validators.ts +++ b/packages/tenant-process/src/services/validators.ts @@ -1,10 +1,8 @@ import { AuthData, userRoles } from "pagopa-interop-commons"; import { - AgreementState, Attribute, AttributeId, CONTRACT_AUTHORITY_PUBLIC_SERVICES_MANAGERS, - EService, ExternalId, PUBLIC_ADMINISTRATIONS_IDENTIFIER, PUBLIC_SERVICES_MANAGERS, @@ -20,6 +18,7 @@ import { tenantAttributeType, tenantKind, SCP, + Agreement, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -33,6 +32,8 @@ import { attributeNotFound, tenantAlreadyHasDelegatedProducerFeature, tenantHasNoDelegatedProducerFeature, + eServiceNotFound, + descriptorNotFoundInEservice, } from "../model/domain/errors.js"; import { ReadModelService } from "./readModelService.js"; @@ -47,60 +48,60 @@ export function assertVerifiedAttributeExistsInTenant( } export async function assertVerifiedAttributeOperationAllowed({ - producerId, + requesterId, + delegateProducerId, consumerId, attributeId, - agreementStates, + agreement, readModelService, error, }: { - producerId: TenantId; + requesterId: TenantId; + delegateProducerId: TenantId | undefined; consumerId: TenantId; attributeId: AttributeId; - agreementStates: AgreementState[]; + agreement: Agreement; readModelService: ReadModelService; error: Error; }): Promise { - if (producerId === consumerId) { + if ([requesterId, delegateProducerId].includes(consumerId)) { throw verifiedAttributeSelfVerificationNotAllowed(); } - // Get agreements - const agreements = await readModelService.getAgreements({ - consumerId, - producerId, - states: agreementStates, - }); - // Extract descriptor IDs - const descriptorIds = agreements.map((agreement) => agreement.descriptorId); + const descriptorId = agreement.descriptorId; - // Get eServices concurrently - const eServices = ( - await Promise.all( - agreements.map((agreement) => - readModelService.getEServiceById(agreement.eserviceId) - ) - ) - ).filter((eService): eService is EService => eService !== undefined); + const eservice = await readModelService.getEServiceById(agreement.eserviceId); + + if (!eservice) { + throw eServiceNotFound(agreement.eserviceId); + } + + const descriptor = eservice.descriptors.find( + (descriptor) => descriptor.id === descriptorId + ); + + if (!descriptor) { + throw descriptorNotFoundInEservice(eservice.id, descriptorId); + } - // Find verified attribute IDs const attributeIds = new Set( - eServices - .flatMap((eService) => - eService.descriptors.filter((descriptor) => - descriptorIds.includes(descriptor.id) - ) - ) - .flatMap((descriptor) => - descriptor.attributes.verified.flatMap((attribute) => - attribute.map((a) => a.id) - ) - ) + descriptor.attributes.verified.flatMap((attribute) => + attribute.map((a) => a.id) + ) ); + // Check if attribute is allowed if (!attributeIds.has(attributeId)) { throw error; } + + if (delegateProducerId && delegateProducerId !== requesterId) { + throw error; + } + + if (!delegateProducerId && requesterId !== agreement.producerId) { + throw error; + } } export function assertOrganizationVerifierExist( diff --git a/packages/tenant-process/src/utilities/errorMappers.ts b/packages/tenant-process/src/utilities/errorMappers.ts index f34e04ec9e..aac5032b8e 100644 --- a/packages/tenant-process/src/utilities/errorMappers.ts +++ b/packages/tenant-process/src/utilities/errorMappers.ts @@ -149,6 +149,9 @@ export const verifyVerifiedAttributeErrorMapper = ( match(error.code) .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) .with("attributeNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("agreementNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("eServiceNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("descriptorNotFoundInEservice", () => HTTP_STATUS_NOT_FOUND) .with( "verifiedAttributeSelfVerificationNotAllowed", () => HTTP_STATUS_FORBIDDEN @@ -162,6 +165,9 @@ export const revokeVerifiedAttributeErrorMapper = ( match(error.code) .with("tenantNotFound", () => HTTP_STATUS_NOT_FOUND) .with("attributeNotFound", () => HTTP_STATUS_BAD_REQUEST) + .with("agreementNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("eServiceNotFound", () => HTTP_STATUS_NOT_FOUND) + .with("descriptorNotFoundInEservice", () => HTTP_STATUS_NOT_FOUND) .with( "verifiedAttributeSelfRevocationNotAllowed", "attributeRevocationNotAllowed", diff --git a/packages/tenant-process/test/getTenants.test.ts b/packages/tenant-process/test/getTenants.test.ts index 0be947c0fe..45c453f391 100644 --- a/packages/tenant-process/test/getTenants.test.ts +++ b/packages/tenant-process/test/getTenants.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { Tenant } from "pagopa-interop-models"; +import { Tenant, tenantFeatureType } from "pagopa-interop-models"; import { getMockTenant } from "pagopa-interop-commons-test"; import { addOneTenant, readModelService } from "./utils.js"; @@ -30,8 +30,9 @@ describe("getTenants", () => { await addOneTenant(tenant2); await addOneTenant(tenant3); - const tenantsByName = await readModelService.getTenantsByName({ + const tenantsByName = await readModelService.getTenants({ name: undefined, + features: [], offset: 0, limit: 50, }); @@ -43,17 +44,115 @@ describe("getTenants", () => { await addOneTenant(tenant2); - const tenantsByName = await readModelService.getTenantsByName({ + const tenantsByName = await readModelService.getTenants({ name: "Tenant 1", + features: [], offset: 0, limit: 50, }); expect(tenantsByName.totalCount).toBe(1); expect(tenantsByName.results).toEqual([tenant1]); }); + it("should get tenants by feature", async () => { + const tenantDelegatedProducer1: Tenant = { + ...tenant1, + features: [ + { + type: tenantFeatureType.delegatedProducer, + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(tenantDelegatedProducer1); + + const tenantDelegatedProducer2: Tenant = { + ...tenant2, + features: [ + { + type: tenantFeatureType.delegatedProducer, + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(tenantDelegatedProducer2); + + const tenantCertifier1 = { + ...tenant3, + features: [ + { + type: tenantFeatureType.persistentCertifier, + certifierId: "certifierId", + }, + ], + }; + + await addOneTenant(tenantCertifier1); + await addOneTenant(tenant4); + + const tenantsByName = await readModelService.getTenants({ + name: undefined, + features: [tenantFeatureType.delegatedProducer], + offset: 0, + limit: 50, + }); + expect(tenantsByName.totalCount).toBe(2); + expect(tenantsByName.results).toEqual([ + tenantDelegatedProducer1, + tenantDelegatedProducer2, + ]); + }); + it("should get tenants by feature and name", async () => { + const tenantDelegatedProducer1: Tenant = { + ...tenant1, + features: [ + { + type: tenantFeatureType.delegatedProducer, + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(tenantDelegatedProducer1); + + const tenantDelegatedProducer2: Tenant = { + ...tenant2, + features: [ + { + type: tenantFeatureType.delegatedProducer, + availabilityTimestamp: new Date(), + }, + ], + }; + await addOneTenant(tenantDelegatedProducer2); + + const tenantCertifier1 = { + ...tenant3, + features: [ + { + type: tenantFeatureType.persistentCertifier, + certifierId: "certifierId", + }, + ], + }; + + await addOneTenant(tenantCertifier1); + await addOneTenant(tenant4); + + const tenantsByName = await readModelService.getTenants({ + name: "Tenant 2", + features: [ + tenantFeatureType.delegatedProducer, + tenantFeatureType.persistentCertifier, + ], + offset: 0, + limit: 50, + }); + expect(tenantsByName.totalCount).toBe(1); + expect(tenantsByName.results).toEqual([tenantDelegatedProducer2]); + }); it("should not get tenants if there are not any tenants", async () => { - const tenantsByName = await readModelService.getTenantsByName({ + const tenantsByName = await readModelService.getTenants({ name: undefined, + features: [], offset: 0, limit: 50, }); @@ -65,8 +164,9 @@ describe("getTenants", () => { await addOneTenant(tenant2); - const tenantsByName = await readModelService.getTenantsByName({ + const tenantsByName = await readModelService.getTenants({ name: "Tenant 6", + features: [], offset: 0, limit: 50, }); @@ -79,8 +179,9 @@ describe("getTenants", () => { await addOneTenant(tenant3); await addOneTenant(tenant4); await addOneTenant(tenant5); - const tenantsByName = await readModelService.getTenantsByName({ + const tenantsByName = await readModelService.getTenants({ name: undefined, + features: [], offset: 0, limit: 4, }); @@ -92,8 +193,9 @@ describe("getTenants", () => { await addOneTenant(tenant3); await addOneTenant(tenant4); await addOneTenant(tenant5); - const tenantsByName = await readModelService.getTenantsByName({ + const tenantsByName = await readModelService.getTenants({ name: undefined, + features: [], offset: 2, limit: 4, }); diff --git a/packages/tenant-process/test/revokeVerifiedAttribute.test.ts b/packages/tenant-process/test/revokeVerifiedAttribute.test.ts index 56b3a89480..144ac351b3 100644 --- a/packages/tenant-process/test/revokeVerifiedAttribute.test.ts +++ b/packages/tenant-process/test/revokeVerifiedAttribute.test.ts @@ -10,18 +10,18 @@ import { tenantAttributeType, TenantVerifiedAttributeRevokedV2, Agreement, - toReadModelEService, - toReadModelAgreement, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { describe, it, expect, vi, afterAll, beforeAll } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { - writeInReadmodel, readLastEventByStreamId, getMockAuthData, getMockDescriptor, getMockTenant, getMockEService, + getMockDelegation, } from "pagopa-interop-commons-test"; import { tenantNotFound, @@ -36,10 +36,11 @@ import { getMockVerifiedTenantAttribute, getMockVerifiedBy, getMockRevokedBy, - eservices, - agreements, tenantService, postgresDB, + addOneEService, + addOneAgreement, + addOneDelegation, } from "./utils.js"; describe("revokeVerifiedAttribute", async () => { @@ -75,6 +76,13 @@ describe("revokeVerifiedAttribute", async () => { consumerId: targetTenant.id, }); + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eService.id, + delegateId: revokerTenant.id, + state: delegationState.active, + }); + beforeAll(async () => { vi.useFakeTimers(); vi.setSystemTime(new Date()); @@ -84,92 +92,110 @@ describe("revokeVerifiedAttribute", async () => { vi.useRealTimers(); }); - it("Should revoke the VerifiedAttribute if it exist", async () => { - const mockVerifiedBy = getMockVerifiedBy(); - const tenantWithVerifiedAttribute: Tenant = { - ...targetTenant, - attributes: [ - { - ...verifiedAttribute, - assignmentTimestamp: new Date(), - verifiedBy: [ - { - ...mockVerifiedBy, - id: revokerTenant.id, - }, - ], - revokedBy: [], - }, - ], - updatedAt: new Date(), - }; - - await addOneTenant(tenantWithVerifiedAttribute); - await addOneTenant(revokerTenant); - await writeInReadmodel(toReadModelEService(eService), eservices); - await writeInReadmodel(toReadModelAgreement(agreementEservice), agreements); + it.each([ + { + desc: "without delegation", + hasDelegation: false, + }, + { + desc: "with delegation", + hasDelegation: true, + }, + ])( + "Should revoke the VerifiedAttribute if it exist $desc", + async (hasDelegation) => { + const mockVerifiedBy = getMockVerifiedBy(); + const tenantWithVerifiedAttribute: Tenant = { + ...targetTenant, + attributes: [ + { + ...verifiedAttribute, + assignmentTimestamp: new Date(), + verifiedBy: [ + { + ...mockVerifiedBy, + id: revokerTenant.id, + }, + ], + revokedBy: [], + }, + ], + updatedAt: new Date(), + }; - const returnedTenant = await tenantService.revokeVerifiedAttribute( - { - tenantId: tenantWithVerifiedAttribute.id, - attributeId: verifiedAttribute.id, - }, - { - authData, - correlationId: generateId(), - serviceName: "", - logger: genericLogger, + await addOneTenant(tenantWithVerifiedAttribute); + await addOneTenant(revokerTenant); + await addOneEService(eService); + await addOneAgreement(agreementEservice); + if (hasDelegation) { + await addOneDelegation(delegation); } - ); - const writtenEvent = await readLastEventByStreamId( - tenantWithVerifiedAttribute.id, - "tenant", - postgresDB - ); + const returnedTenant = await tenantService.revokeVerifiedAttribute( + { + tenantId: tenantWithVerifiedAttribute.id, + attributeId: verifiedAttribute.id, + agreementId: agreementEservice.id, + }, + { + authData, + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); - expect(writtenEvent).toMatchObject({ - stream_id: tenantWithVerifiedAttribute.id, - version: "1", - type: "TenantVerifiedAttributeRevoked", - event_version: 2, - }); + const writtenEvent = await readLastEventByStreamId( + tenantWithVerifiedAttribute.id, + "tenant", + postgresDB + ); - const writtenPayload = protobufDecoder( - TenantVerifiedAttributeRevokedV2 - ).parse(writtenEvent?.data); + expect(writtenEvent).toMatchObject({ + stream_id: tenantWithVerifiedAttribute.id, + version: "1", + type: "TenantVerifiedAttributeRevoked", + event_version: 2, + }); - const updatedTenant: Tenant = { - ...tenantWithVerifiedAttribute, - attributes: [ - { - id: verifiedAttribute.id, - type: tenantAttributeType.VERIFIED, - assignmentTimestamp: new Date(), - verifiedBy: [], - revokedBy: [ - { - id: revokerTenant.id, - verificationDate: mockVerifiedBy.verificationDate, - revocationDate: new Date(), - }, - ], - }, - ], - updatedAt: new Date(), - }; + const writtenPayload = protobufDecoder( + TenantVerifiedAttributeRevokedV2 + ).parse(writtenEvent?.data); - expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); - expect(returnedTenant).toEqual(updatedTenant); - }); + const updatedTenant: Tenant = { + ...tenantWithVerifiedAttribute, + attributes: [ + { + id: verifiedAttribute.id, + type: tenantAttributeType.VERIFIED, + assignmentTimestamp: new Date(), + verifiedBy: [], + revokedBy: [ + { + id: hasDelegation ? delegation.delegatorId : revokerTenant.id, + delegationId: hasDelegation ? delegation.id : undefined, + verificationDate: mockVerifiedBy.verificationDate, + revocationDate: new Date(), + }, + ], + }, + ], + updatedAt: new Date(), + }; + + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + expect(returnedTenant).toEqual(updatedTenant); + } + ); it("Should throw tenantNotFound if the tenant doesn't exist", async () => { - await writeInReadmodel(toReadModelEService(eService), eservices); - await writeInReadmodel(toReadModelAgreement(agreementEservice), agreements); + await addOneEService(eService); + await addOneAgreement(agreementEservice); expect( tenantService.revokeVerifiedAttribute( { tenantId: targetTenant.id, attributeId: verifiedAttribute.id, + agreementId: agreementEservice.id, }, { authData, @@ -195,13 +221,14 @@ describe("revokeVerifiedAttribute", async () => { await addOneTenant(tenantWithoutSameAttributeId); await addOneTenant(revokerTenant); - await writeInReadmodel(toReadModelEService(eService), eservices); - await writeInReadmodel(toReadModelAgreement(agreementEservice), agreements); + await addOneEService(eService); + await addOneAgreement(agreementEservice); expect( tenantService.revokeVerifiedAttribute( { tenantId: tenantWithoutSameAttributeId.id, attributeId: verifiedAttribute.id, + agreementId: agreementEservice.id, }, { authData, @@ -231,14 +258,15 @@ describe("revokeVerifiedAttribute", async () => { await addOneTenant(tenantWithVerifiedAttribute); await addOneTenant(revokerTenant); - await writeInReadmodel(toReadModelEService(eService), eservices); - await writeInReadmodel(toReadModelAgreement(agreementEservice), agreements); + await addOneEService(eService); + await addOneAgreement(agreementEservice); expect( tenantService.revokeVerifiedAttribute( { tenantId: tenantWithVerifiedAttribute.id, attributeId: verifiedAttribute.id, + agreementId: agreementEservice.id, }, { authData, @@ -253,14 +281,15 @@ describe("revokeVerifiedAttribute", async () => { }); it("Should throw verifiedAttributeSelfRevocationNotAllowed when trying to revoke own attributes", async () => { await addOneTenant(revokerTenant); - await writeInReadmodel(toReadModelEService(eService), eservices); - await writeInReadmodel(toReadModelAgreement(agreementEservice), agreements); + await addOneEService(eService); + await addOneAgreement(agreementEservice); expect( tenantService.revokeVerifiedAttribute( { tenantId: revokerTenant.id, attributeId: verifiedAttribute.id, + agreementId: agreementEservice.id, }, { authData, @@ -285,14 +314,15 @@ describe("revokeVerifiedAttribute", async () => { await addOneTenant(tenantWithVerifiedAttribute); await addOneTenant(revokerTenant); - await writeInReadmodel(toReadModelEService(eService), eservices); - await writeInReadmodel(toReadModelAgreement(agreementEservice), agreements); + await addOneEService(eService); + await addOneAgreement(agreementEservice); expect( tenantService.revokeVerifiedAttribute( { tenantId: tenantWithVerifiedAttribute.id, attributeId: verifiedAttribute.id, + agreementId: agreementEservice.id, }, { authData, diff --git a/packages/tenant-process/test/utils.ts b/packages/tenant-process/test/utils.ts index a3871f3df7..1af53b89bf 100644 --- a/packages/tenant-process/test/utils.ts +++ b/packages/tenant-process/test/utils.ts @@ -19,6 +19,7 @@ import { EServiceId, DescriptorId, agreementState, + Delegation, } from "pagopa-interop-models"; import { ReadEvent, @@ -40,8 +41,14 @@ export const { cleanup, readModelRepository, postgresDB } = afterEach(cleanup); -export const { agreements, clients, eservices, attributes, tenants } = - readModelRepository; +export const { + agreements, + clients, + eservices, + attributes, + tenants, + delegations, +} = readModelRepository; export const readModelService = readModelServiceBuilder(readModelRepository); @@ -143,6 +150,12 @@ export const addOneTenant = async (tenant: Tenant): Promise => { await writeInReadmodel(toReadModelTenant(tenant), tenants); }; +export const addOneDelegation = async ( + delegation: Delegation +): Promise => { + await writeInReadmodel(delegation, delegations); +}; + export const readLastTenantEvent = async ( tenantId: TenantId ): Promise> => diff --git a/packages/tenant-process/test/verifyVerifiedAttribute.test.ts b/packages/tenant-process/test/verifyVerifiedAttribute.test.ts index 28dfb8fda7..59dc8efb4a 100644 --- a/packages/tenant-process/test/verifyVerifiedAttribute.test.ts +++ b/packages/tenant-process/test/verifyVerifiedAttribute.test.ts @@ -13,19 +13,18 @@ import { Attribute, attributeKind, Agreement, - toReadModelAttribute, - toReadModelEService, - toReadModelAgreement, + delegationState, + delegationKind, } from "pagopa-interop-models"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; import { - writeInReadmodel, getMockAttribute, readLastEventByStreamId, getMockDescriptor, getMockEService, getMockTenant, + getMockDelegation, } from "pagopa-interop-commons-test"; import { tenantApi } from "pagopa-interop-api-clients"; import { @@ -41,21 +40,22 @@ import { getMockVerifiedBy, getMockRevokedBy, tenantService, - attributes, - eservices, - agreements, postgresDB, + addOneAgreement, + addOneAttribute, + addOneEService, + addOneDelegation, } from "./utils.js"; describe("verifyVerifiedAttribute", async () => { const targetTenant = getMockTenant(); const requesterTenant = getMockTenant(); - const tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed = { - id: generateId(), - }; + + const tenantAttributeSeedId = generateId(); + const attribute: Attribute = { ...getMockAttribute(), - id: unsafeBrandId(tenantAttributeSeed.id), + id: unsafeBrandId(tenantAttributeSeedId), kind: attributeKind.verified, }; const descriptor1: Descriptor = { @@ -65,7 +65,7 @@ describe("verifyVerifiedAttribute", async () => { verified: [ [ { - id: unsafeBrandId(tenantAttributeSeed.id), + id: unsafeBrandId(tenantAttributeSeedId), explicitAttributeVerification: false, }, ], @@ -85,6 +85,17 @@ describe("verifyVerifiedAttribute", async () => { producerId: eService1.producerId, consumerId: targetTenant.id, }); + const tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed = { + id: tenantAttributeSeedId, + agreementId: agreementEservice1.id, + }; + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: eService1.id, + delegateId: requesterTenant.id, + state: delegationState.active, + }); beforeAll(async () => { vi.useFakeTimers(); @@ -95,152 +106,178 @@ describe("verifyVerifiedAttribute", async () => { vi.useRealTimers(); }); - it("Should verify the VerifiedAttribute if verifiedTenantAttribute doesn't exist", async () => { - await addOneTenant(targetTenant); - await addOneTenant(requesterTenant); - await writeInReadmodel(toReadModelAttribute(attribute), attributes); - await writeInReadmodel(toReadModelEService(eService1), eservices); - await writeInReadmodel( - toReadModelAgreement(agreementEservice1), - agreements - ); - const returnedTenant = await tenantService.verifyVerifiedAttribute( - { - tenantId: targetTenant.id, - tenantAttributeSeed, - organizationId: requesterTenant.id, - correlationId: generateId(), - }, - genericLogger - ); - - const writtenEvent = await readLastEventByStreamId( - targetTenant.id, - "tenant", - postgresDB - ); - - expect(writtenEvent).toMatchObject({ - stream_id: targetTenant.id, - version: "1", - type: "TenantVerifiedAttributeAssigned", - event_version: 2, - }); - const writtenPayload = protobufDecoder( - TenantVerifiedAttributeAssignedV2 - ).parse(writtenEvent?.data); + it.each([ + { + desc: "without delegation", + hasDelegation: false, + }, + { + desc: "with delegation", + hasDelegation: true, + }, + ])( + "Should verify the VerifiedAttribute if verifiedTenantAttribute doesn't exist $desc", + async ({ hasDelegation }) => { + await addOneTenant(targetTenant); + await addOneTenant(requesterTenant); + await addOneAttribute(attribute); + await addOneEService(eService1); + await addOneAgreement(agreementEservice1); + if (hasDelegation) { + await addOneDelegation(delegation); + } - const updatedTenant: Tenant = { - ...targetTenant, - attributes: [ + const returnedTenant = await tenantService.verifyVerifiedAttribute( { - id: unsafeBrandId(tenantAttributeSeed.id), - type: tenantAttributeType.VERIFIED, - assignmentTimestamp: new Date(), - verifiedBy: [ - { - id: requesterTenant.id, - verificationDate: new Date(), - expirationDate: tenantAttributeSeed.expirationDate - ? new Date(tenantAttributeSeed.expirationDate) - : undefined, - extensionDate: tenantAttributeSeed.expirationDate - ? new Date(tenantAttributeSeed.expirationDate) - : undefined, - }, - ], - revokedBy: [], + tenantId: targetTenant.id, + tenantAttributeSeed, + organizationId: requesterTenant.id, + correlationId: generateId(), }, - ], - updatedAt: new Date(), - }; - expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); - expect(returnedTenant).toEqual(updatedTenant); - }); - it("Should verify the VerifiedAttribute if verifiedTenantAttribute exist", async () => { - const mockVerifiedBy = getMockVerifiedBy(); - const mockRevokedBy = getMockRevokedBy(); + genericLogger + ); - const tenantWithVerifiedAttribute: Tenant = { - ...targetTenant, - attributes: [ - { - ...getMockVerifiedTenantAttribute(), - id: attribute.id, - verifiedBy: [ - { - ...mockVerifiedBy, - }, - ], - revokedBy: [{ ...mockRevokedBy }], - }, - ], - }; + const writtenEvent = await readLastEventByStreamId( + targetTenant.id, + "tenant", + postgresDB + ); - await addOneTenant(tenantWithVerifiedAttribute); - await addOneTenant(requesterTenant); - await writeInReadmodel(toReadModelAttribute(attribute), attributes); - await writeInReadmodel(toReadModelEService(eService1), eservices); - await writeInReadmodel( - toReadModelAgreement(agreementEservice1), - agreements - ); + expect(writtenEvent).toMatchObject({ + stream_id: targetTenant.id, + version: "1", + type: "TenantVerifiedAttributeAssigned", + event_version: 2, + }); + const writtenPayload = protobufDecoder( + TenantVerifiedAttributeAssignedV2 + ).parse(writtenEvent?.data); - const returnedTenant = await tenantService.verifyVerifiedAttribute( - { - tenantId: tenantWithVerifiedAttribute.id, - tenantAttributeSeed, - organizationId: requesterTenant.id, - correlationId: generateId(), - }, - genericLogger - ); - const writtenEvent = await readLastEventByStreamId( - tenantWithVerifiedAttribute.id, - "tenant", - postgresDB - ); + const updatedTenant: Tenant = { + ...targetTenant, + attributes: [ + { + id: unsafeBrandId(tenantAttributeSeed.id), + type: tenantAttributeType.VERIFIED, + assignmentTimestamp: new Date(), + verifiedBy: [ + { + id: hasDelegation ? delegation.delegatorId : requesterTenant.id, + delegationId: hasDelegation ? delegation.id : undefined, + verificationDate: new Date(), + expirationDate: tenantAttributeSeed.expirationDate + ? new Date(tenantAttributeSeed.expirationDate) + : undefined, + extensionDate: tenantAttributeSeed.expirationDate + ? new Date(tenantAttributeSeed.expirationDate) + : undefined, + }, + ], + revokedBy: [], + }, + ], + updatedAt: new Date(), + }; + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + expect(returnedTenant).toEqual(updatedTenant); + } + ); - expect(writtenEvent).toMatchObject({ - stream_id: tenantWithVerifiedAttribute.id, - version: "1", - type: "TenantVerifiedAttributeAssigned", - event_version: 2, - }); - const writtenPayload = protobufDecoder( - TenantVerifiedAttributeAssignedV2 - ).parse(writtenEvent?.data); + it.each([ + { + desc: "without delegation", + hasDelegation: false, + }, + { + desc: "with delegation", + hasDelegation: true, + }, + ])( + "Should verify the VerifiedAttribute if verifiedTenantAttribute exist $desc", + async (hasDelegation) => { + const mockVerifiedBy = getMockVerifiedBy(); + const mockRevokedBy = getMockRevokedBy(); + + const tenantWithVerifiedAttribute: Tenant = { + ...targetTenant, + attributes: [ + { + ...getMockVerifiedTenantAttribute(), + id: attribute.id, + verifiedBy: [ + { + ...mockVerifiedBy, + }, + ], + revokedBy: [{ ...mockRevokedBy }], + }, + ], + }; - const updatedTenant: Tenant = { - ...tenantWithVerifiedAttribute, - attributes: [ + await addOneTenant(tenantWithVerifiedAttribute); + await addOneTenant(requesterTenant); + await addOneAttribute(attribute); + await addOneEService(eService1); + await addOneAgreement(agreementEservice1); + if (hasDelegation) { + await addOneDelegation(delegation); + } + + const returnedTenant = await tenantService.verifyVerifiedAttribute( { - id: attribute.id, - type: "PersistentVerifiedAttribute", - assignmentTimestamp: new Date(), - verifiedBy: [ - { ...mockVerifiedBy }, - { - ...mockVerifiedBy, - id: requesterTenant.id, - verificationDate: new Date(), - }, - ], - revokedBy: [{ ...mockRevokedBy }], + tenantId: tenantWithVerifiedAttribute.id, + tenantAttributeSeed, + organizationId: requesterTenant.id, + correlationId: generateId(), }, - ], - updatedAt: new Date(), - }; + genericLogger + ); + const writtenEvent = await readLastEventByStreamId( + tenantWithVerifiedAttribute.id, + "tenant", + postgresDB + ); - expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); - expect(returnedTenant).toEqual(updatedTenant); - }); + expect(writtenEvent).toMatchObject({ + stream_id: tenantWithVerifiedAttribute.id, + version: "1", + type: "TenantVerifiedAttributeAssigned", + event_version: 2, + }); + const writtenPayload = protobufDecoder( + TenantVerifiedAttributeAssignedV2 + ).parse(writtenEvent?.data); + + const updatedTenant: Tenant = { + ...tenantWithVerifiedAttribute, + attributes: [ + { + id: attribute.id, + type: "PersistentVerifiedAttribute", + assignmentTimestamp: new Date(), + verifiedBy: [ + { ...mockVerifiedBy }, + { + ...mockVerifiedBy, + id: hasDelegation ? delegation.delegatorId : requesterTenant.id, + delegationId: hasDelegation ? delegation.id : undefined, + verificationDate: new Date(), + }, + ], + revokedBy: [{ ...mockRevokedBy }], + }, + ], + updatedAt: new Date(), + }; + + expect(writtenPayload.tenant).toEqual(toTenantV2(updatedTenant)); + expect(returnedTenant).toEqual(updatedTenant); + } + ); it("Should throw tenantNotFound if the tenant doesn't exist", async () => { - await writeInReadmodel(toReadModelEService(eService1), eservices); - await writeInReadmodel( - toReadModelAgreement(agreementEservice1), - agreements - ); + await addOneEService(eService1); + await addOneAgreement(agreementEservice1); + expect( tenantService.verifyVerifiedAttribute( { @@ -256,11 +293,9 @@ describe("verifyVerifiedAttribute", async () => { it("Should throw attributeNotFound if the attribute doesn't exist", async () => { await addOneTenant(targetTenant); await addOneTenant(requesterTenant); - await writeInReadmodel(toReadModelEService(eService1), eservices); - await writeInReadmodel( - toReadModelAgreement(agreementEservice1), - agreements - ); + await addOneEService(eService1); + await addOneAgreement(agreementEservice1); + expect( tenantService.verifyVerifiedAttribute( { @@ -303,20 +338,17 @@ describe("verifyVerifiedAttribute", async () => { await addOneTenant(targetTenant); await addOneTenant(requesterTenant); - await writeInReadmodel( - toReadModelEService(eServiceWithNotAllowedDescriptor), - eservices - ); - await writeInReadmodel( - toReadModelAgreement(agreementEserviceWithNotAllowedDescriptor), - agreements - ); + await addOneEService(eServiceWithNotAllowedDescriptor); + await addOneAgreement(agreementEserviceWithNotAllowedDescriptor); expect( tenantService.verifyVerifiedAttribute( { tenantId: targetTenant.id, - tenantAttributeSeed, + tenantAttributeSeed: { + ...tenantAttributeSeed, + agreementId: agreementEserviceWithNotAllowedDescriptor.id, + }, organizationId: requesterTenant.id, correlationId: generateId(), }, @@ -332,11 +364,8 @@ describe("verifyVerifiedAttribute", async () => { it("Should throw verifiedAttributeSelfVerificationNotAllowed if the organizations are not allowed to revoke own attributes", async () => { await addOneTenant(targetTenant); await addOneTenant(requesterTenant); - await writeInReadmodel(toReadModelEService(eService1), eservices); - await writeInReadmodel( - toReadModelAgreement(agreementEservice1), - agreements - ); + await addOneEService(eService1); + await addOneAgreement(agreementEservice1); expect( tenantService.verifyVerifiedAttribute( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15c301e4ca..eeed99ff12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.10 - version: 1.0.10 + specifier: 1.0.11a + version: 1.0.11-a '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1063,8 +1063,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.10 - version: 1.0.10 + specifier: 1.0.11a + version: 1.0.11-a '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2537,8 +2537,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.10 - version: 1.0.10 + specifier: 1.0.11a + version: 1.0.11-a '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2842,8 +2842,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.10 - version: 1.0.10 + specifier: 1.0.11a + version: 1.0.11-a '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3905,8 +3905,8 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.10': - resolution: {integrity: sha512-/TDVP8j+Q0ErpVs7MzH9LhfiEPcOzqea00um4sjQ9uuA6/Yg0G9A739bDOuaxih2wKpJ3hNPLnm9riM+YAz6Ow==} + '@pagopa/interop-outbound-models@1.0.11-a': + resolution: {integrity: sha512-+hxMAO2ywfH03PZ4iMUn7VHiGEtQUiEa2cEqLp12s6y69b7ClM3dg0ZxTuXnGLUqiiLX4sbSAy26rc8IG0sIdg==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -9211,7 +9211,7 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.10': + '@pagopa/interop-outbound-models@1.0.11-a': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 From 16706e9bde1730f3ba04db78666c04560c928740 Mon Sep 17 00:00:00 2001 From: paologaleotti <45665769+paologaleotti@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:50:48 +0100 Subject: [PATCH 053/126] PIN-5667 add tests for GET delegations (#1227) --- .../test/getDelegations.test.ts | 100 +++++++++++++++--- 1 file changed, 88 insertions(+), 12 deletions(-) diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts index 0a11027266..2d22269099 100644 --- a/packages/delegation-process/test/getDelegations.test.ts +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -6,14 +6,90 @@ import { delegationKind } from "pagopa-interop-models"; import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegations", () => { - it("should get delegations", async () => { - const delegation1 = getMockDelegation({ - kind: delegationKind.delegatedProducer, - state: "Active", - }); - const delegation2 = getMockDelegation({ - kind: delegationKind.delegatedProducer, - }); + it("should get producer's delegations", async () => { + const kind = delegationKind.delegatedProducer; + + const delegation1 = getMockDelegation({ kind, state: "Active" }); + const delegation2 = getMockDelegation({ kind }); + await addOneDelegation(delegation1); + await addOneDelegation(delegation2); + + const res1 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind, + offset: 0, + limit: 50, + }, + genericLogger + ); + expect(res1).toEqual([delegation1]); + + const res2 = await delegationService.getDelegations( + { + delegateIds: [delegation2.delegateId], + delegatorIds: [], + delegationStates: [], + eserviceIds: [], + kind, + offset: 0, + limit: 50, + }, + genericLogger + ); + expect(res2).toEqual([delegation2]); + + const res3 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Revoked"], + eserviceIds: [], + kind, + offset: 0, + limit: 50, + }, + genericLogger + ); + expect(res3).toEqual([]); + + const res4 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [], + kind: undefined, + offset: 0, + limit: 50, + }, + genericLogger + ); + expect(res4).toEqual([delegation1]); + + const res5 = await delegationService.getDelegations( + { + delegateIds: [], + delegatorIds: [], + delegationStates: ["Active"], + eserviceIds: [delegation1.eserviceId], + kind, + offset: 0, + limit: 50, + }, + genericLogger + ); + expect(res5).toEqual([delegation1]); + }); + + it("should get consumer's delegations", async () => { + const kind = delegationKind.delegatedConsumer; + + const delegation1 = getMockDelegation({ kind, state: "Active" }); + const delegation2 = getMockDelegation({ kind }); await addOneDelegation(delegation1); await addOneDelegation(delegation2); @@ -23,7 +99,7 @@ describe("get delegations", () => { delegatorIds: [], delegationStates: ["Active"], eserviceIds: [], - kind: "DelegatedProducer", + kind, offset: 0, limit: 50, }, @@ -37,7 +113,7 @@ describe("get delegations", () => { delegatorIds: [], delegationStates: [], eserviceIds: [], - kind: "DelegatedProducer", + kind, offset: 0, limit: 50, }, @@ -51,7 +127,7 @@ describe("get delegations", () => { delegatorIds: [], delegationStates: ["Revoked"], eserviceIds: [], - kind: "DelegatedProducer", + kind, offset: 0, limit: 50, }, @@ -79,7 +155,7 @@ describe("get delegations", () => { delegatorIds: [], delegationStates: ["Active"], eserviceIds: [delegation1.eserviceId], - kind: "DelegatedProducer", + kind, offset: 0, limit: 50, }, From 97fa459c34e989bcb9ea476b443876d2aa0d519e Mon Sep 17 00:00:00 2001 From: Vittorio Caprio Date: Tue, 3 Dec 2024 14:30:00 +0100 Subject: [PATCH 054/126] Fix Puppeteer dependencies in Docker file for delegation process (#1248) --- packages/delegation-process/Dockerfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/delegation-process/Dockerfile b/packages/delegation-process/Dockerfile index 926e4fab45..7b5947a0ab 100644 --- a/packages/delegation-process/Dockerfile +++ b/packages/delegation-process/Dockerfile @@ -37,8 +37,22 @@ RUN pnpm build && \ FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final +# Install dependencies for Puppeteer +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + chromium \ + ca-certificates + +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium + +RUN groupadd -r app && useradd -rm -g app -G audio,video app + COPY --from=build /out /app +RUN chown -R app:app /app +RUN chmod -R 700 /app +USER app + WORKDIR /app/packages/delegation-process EXPOSE 3000 From dac1a6c47564c8c7efad1c1637928fe7586df523 Mon Sep 17 00:00:00 2001 From: Vittorio Caprio Date: Tue, 3 Dec 2024 17:50:08 +0100 Subject: [PATCH 055/126] Add missing delegation info in agreement template (#1243) --- .../documents/agreementContractTemplate.html | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html index 850606c159..ffcf79d2c8 100644 --- a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html +++ b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html @@ -94,7 +94,7 @@

Attributi verificati

{{this.assignmentDate}} alle ore {{this.assignmentTime}}, l’infrastruttura ha registrato - la verifica {{#if delegationId}}del delegato all’erogazione{{else}}dell’erogatore{{/if}} + la verifica {{#if producerDelegationId}}del delegato all’erogazione{{else}}dell’erogatore{{/if}} che il fruitore possegga l’attributo verificato {{this.attributeName}}, contraddistinto da id {{this.attributeId}}, {{#if this.expirationDate}} ed avente scadenza il {{this.expirationDate}},{{/if}} @@ -125,32 +125,20 @@

Registrazione

In data {{submissionDate}} alle ore {{submissionTime}}, l’Infrastruttura ha trasmesso - all’erogatore la richiesta di fruizione. + {{#if producerDelegationId}} al delegante all’erogazione{{else}} all’erogatore{{/if}} la richiesta di fruizione.
- {{#if producerDelegationId}}
In data {{activationDate}} alle ore {{activationTime}}, l’infrastruttura ha ricevuto la dichiarazione di accettazione della richiesta di fruizione - (di seguito “dichiarazione di accettazione”) inviata dal delegato all’erogazione, + (di seguito “dichiarazione di accettazione”) inviata {{#if producerDelegationId}}dal delegato all’erogazione,{{else}}dall’erogatore,{{/if}} tramite l’operatore amministrativo con identificativo Area Riservata - {{producerDelegateId}}. -
- {{else}} -
- In data {{activationDate}} alle ore - {{activationTime}}, l’Infrastruttura ha ricevuto la - dichiarazione di accettazione della richiesta di fruizione (di seguito - “dichiarazione di accettazione”) inviata dall’erogatore, tramite - l’operatore amministrativo con identificativo Area Riservata {{activatorId}}.
- {{/if}} -
In data {{activationDate}} alle ore {{activationTime}}, l’Infrastruttura ha comunicato al - fruitore la dichiarazione di accettazione inviata dall’erogatore. + fruitore la dichiarazione di accettazione inviata {{#if producerDelegationId}}dal delegato all’erogazione{{else}}dall’erogatore{{/if}}.
From 4cfb5c5db1682adcda5f9bd5ded4ce9d96728ae6 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 4 Dec 2024 13:00:14 +0100 Subject: [PATCH 056/126] Fix `getDelegations` url params (#1250) --- packages/api-clients/open-api/bffApi.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 3305a787db..f63b7604a6 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -12363,6 +12363,8 @@ paths: items: type: string format: uuid + default: [] + explode: false description: "The delegator ids to filter by" - in: query name: delegateIds @@ -12371,6 +12373,8 @@ paths: items: type: string format: uuid + default: [] + explode: false description: "The delegated ids to filter by" - in: query name: kind From 7cc29d3407bef5f1e58f985acb87ea78669b45f6 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Wed, 4 Dec 2024 14:49:56 +0100 Subject: [PATCH 057/126] PIN-5643: Added missing SCP in tenantKind in purpose api (#1226) --- packages/api-clients/open-api/purposeApi.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/api-clients/open-api/purposeApi.yml b/packages/api-clients/open-api/purposeApi.yml index 75f81ad1fa..27df3fcd13 100644 --- a/packages/api-clients/open-api/purposeApi.yml +++ b/packages/api-clients/open-api/purposeApi.yml @@ -756,6 +756,7 @@ components: - PA - PRIVATE - GSP + - SCP RiskAnalysisFormConfigResponse: type: object additionalProperties: false From c12d334f10fd3e142aee6806c0bea5199bf8487a Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 4 Dec 2024 15:28:18 +0100 Subject: [PATCH 058/126] Standardizing agreement requester validators (#1246) --- .../src/model/domain/agreement-validators.ts | 87 ++++++++++--------- .../src/services/agreementService.ts | 34 ++++---- .../src/services/readModelService.ts | 36 +------- .../test/activateAgreement.test.ts | 9 +- .../test/agreementConsumerDocuments.test.ts | 61 ++++++++++++- .../test/rejectAgreement.test.ts | 1 + .../test/suspendAgreement.test.ts | 3 + 7 files changed, 135 insertions(+), 96 deletions(-) diff --git a/packages/agreement-process/src/model/domain/agreement-validators.ts b/packages/agreement-process/src/model/domain/agreement-validators.ts index 8315e5dc29..900e1feb30 100644 --- a/packages/agreement-process/src/model/domain/agreement-validators.ts +++ b/packages/agreement-process/src/model/domain/agreement-validators.ts @@ -17,6 +17,8 @@ import { AgreementStamp, AgreementStamps, delegationKind, + Delegation, + delegationState, } from "pagopa-interop-models"; import { agreementApi } from "pagopa-interop-api-clients"; import { AuthData } from "pagopa-interop-commons"; @@ -137,89 +139,94 @@ export const assertRequesterIsConsumer = ( } }; -export function assertRequesterIsProducer( +const assertRequesterIsProducer = ( agreement: Agreement, authData: AuthData -): void { +): void => { if ( !authData.userRoles.includes("internal") && authData.organizationId !== agreement.producerId ) { throw operationNotAllowed(authData.organizationId); } -} +}; -export const assertRequesterIsConsumerOrProducer = ( +export const assertRequesterCanActAsConsumerOrProducer = ( agreement: Agreement, - authData: AuthData + authData: AuthData, + activeProducerDelegation: Delegation | undefined ): void => { try { assertRequesterIsConsumer(agreement, authData); } catch (error) { - assertRequesterIsProducer(agreement, authData); + assertRequesterCanActAsProducer( + agreement, + authData, + activeProducerDelegation + ); } }; -export const assertRequesterIsConsumerOrProducerOrDelegateProducer = async ( +export const assertRequesterCanRetrieveConsumerDocuments = async ( agreement: Agreement, authData: AuthData, readModelService: ReadModelService ): Promise => { + // This operation has a dedicated assertion because it's the only operation that + // can be performed also by the producer even when an active producer delegation exists try { assertRequesterIsConsumer(agreement, authData); } catch (error) { try { assertRequesterIsProducer(agreement, authData); } catch (error) { - const producerDelegation = - await readModelService.getDelegationByDelegateId( - authData.organizationId, - delegationKind.delegatedProducer + const activeProducerDelegation = + await readModelService.getActiveProducerDelegationByEserviceId( + agreement.eserviceId ); - assertRequesterIsDelegate(producerDelegation?.delegateId, authData); + assertRequesterIsDelegateProducer( + agreement, + authData, + activeProducerDelegation + ); } } }; -export const assertRequesterIsProducerOrDelegateProducer = ( +export const assertRequesterCanActAsProducer = ( agreement: Agreement, - delegateProducerId: TenantId | undefined, - authData: AuthData + authData: AuthData, + activeProducerDelegation: Delegation | undefined ): void => { - if (delegateProducerId) { - assertRequesterIsDelegate(delegateProducerId, authData); - } else { + if (!activeProducerDelegation) { + // No active producer delegation, the requester is authorized only if they are the producer assertRequesterIsProducer(agreement, authData); + } else { + // Active producer delegation, the requester is authorized only if they are the delegate + assertRequesterIsDelegateProducer( + agreement, + authData, + activeProducerDelegation + ); } }; -export const assertRequesterIsDelegate = ( - delegateId: TenantId | undefined, - authData: AuthData -): void => { - if (authData.organizationId !== delegateId) { - throw operationNotAllowed(authData.organizationId); - } -}; - -export const assertRequesterCanActivate = ( +const assertRequesterIsDelegateProducer = ( agreement: Agreement, - delegateProducerId: TenantId | undefined, - authData: AuthData + authData: AuthData, + activeProducerDelegation: Delegation | undefined ): void => { - try { - assertRequesterIsConsumer(agreement, authData); - } catch (e) { - if (delegateProducerId) { - assertRequesterIsDelegate(delegateProducerId, authData); - } else { - assertRequesterIsProducer(agreement, authData); - } + if ( + activeProducerDelegation?.delegateId !== authData.organizationId || + activeProducerDelegation?.delegatorId !== agreement.producerId || + activeProducerDelegation?.kind !== delegationKind.delegatedProducer || + activeProducerDelegation?.state !== delegationState.active || + activeProducerDelegation?.eserviceId !== agreement.eserviceId + ) { + throw operationNotAllowed(authData.organizationId); } }; -export const assertRequesterCanSuspend = assertRequesterCanActivate; - export const assertSubmittableState = ( state: AgreementState, agreementId: AgreementId diff --git a/packages/agreement-process/src/services/agreementService.ts b/packages/agreement-process/src/services/agreementService.ts index a49f88aaca..d18e06c920 100644 --- a/packages/agreement-process/src/services/agreementService.ts +++ b/packages/agreement-process/src/services/agreementService.ts @@ -35,7 +35,6 @@ import { CompactTenant, CorrelationId, Delegation, - delegationKind, } from "pagopa-interop-models"; import { declaredAttributesSatisfied, @@ -84,13 +83,12 @@ import { agreementUpdatableStates, agreementUpgradableStates, assertActivableState, + assertRequesterCanActAsProducer, + assertRequesterCanActAsConsumerOrProducer, + assertRequesterCanRetrieveConsumerDocuments, assertCanWorkOnConsumerDocuments, assertExpectedState, - assertRequesterCanActivate, - assertRequesterCanSuspend, assertRequesterIsConsumer, - assertRequesterIsConsumerOrProducerOrDelegateProducer, - assertRequesterIsProducerOrDelegateProducer, assertSubmittableState, failOnActivationFailure, matchingCertifiedAttributes, @@ -177,10 +175,7 @@ export const retrieveActiveProducerDelegationByEserviceId = async ( eserviceId: EServiceId, readModelService: ReadModelService ): Promise => - await readModelService.getActiveDelegationByEserviceId( - eserviceId, - delegationKind.delegatedProducer - ); + await readModelService.getActiveProducerDelegationByEserviceId(eserviceId); export const retrieveDescriptor = ( descriptorId: DescriptorId, @@ -772,7 +767,7 @@ export function agreementServiceBuilder( ); const agreement = await retrieveAgreement(agreementId, readModelService); - await assertRequesterIsConsumerOrProducerOrDelegateProducer( + await assertRequesterCanRetrieveConsumerDocuments( agreement.data, authData, readModelService @@ -795,7 +790,11 @@ export function agreementServiceBuilder( const delegateProducerId = activeProducerDelegation?.delegateId; - assertRequesterCanSuspend(agreement.data, delegateProducerId, authData); + assertRequesterCanActAsConsumerOrProducer( + agreement.data, + authData, + activeProducerDelegation + ); assertExpectedState( agreementId, @@ -906,12 +905,11 @@ export function agreementServiceBuilder( agreementToBeRejected.data.eserviceId, readModelService ); - const delegateProducerId = activeProducerDelegation?.delegateId; - assertRequesterIsProducerOrDelegateProducer( + assertRequesterCanActAsProducer( agreementToBeRejected.data, - delegateProducerId, - authData + authData, + activeProducerDelegation ); assertExpectedState( @@ -987,7 +985,11 @@ export function agreementServiceBuilder( const delegateProducerId = activeProducerDelegation?.delegateId; - assertRequesterCanActivate(agreement.data, delegateProducerId, authData); + assertRequesterCanActAsConsumerOrProducer( + agreement.data, + authData, + activeProducerDelegation + ); verifyConsumerDoesNotActivatePending(agreement.data, authData); assertActivableState(agreement.data); diff --git a/packages/agreement-process/src/services/readModelService.ts b/packages/agreement-process/src/services/readModelService.ts index c37f51fd97..7dd49e377f 100644 --- a/packages/agreement-process/src/services/readModelService.ts +++ b/packages/agreement-process/src/services/readModelService.ts @@ -25,13 +25,11 @@ import { AttributeReadmodel, TenantId, genericInternalError, - DelegationState, Delegation, delegationState, AgreementReadModel, DescriptorReadModel, EServiceReadModel, - DelegationKind, delegationKind, } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; @@ -615,42 +613,14 @@ export function readModelServiceBuilder( ), }; }, - async getDelegationByDelegateId( - delegateId: TenantId, - kind: DelegationKind, - state: DelegationState = delegationState.active - ): Promise { - const data = await delegations.findOne( - { - "data.delegateId": delegateId, - "data.state": state, - "data.kind": kind, - }, - { projection: { data: true } } - ); - - if (!data) { - return undefined; - } - const result = z.object({ data: Delegation }).safeParse(data); - if (!result.success) { - throw genericInternalError( - `Unable to parse delegation item: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - } - return result.data.data; - }, - async getActiveDelegationByEserviceId( - eserviceId: EServiceId, - kind: DelegationKind + async getActiveProducerDelegationByEserviceId( + eserviceId: EServiceId ): Promise { const data = await delegations.findOne( { "data.eserviceId": eserviceId, "data.state": delegationState.active, - "data.kind": kind, + "data.kind": delegationKind.delegatedProducer, }, { projection: { data: true } } ); diff --git a/packages/agreement-process/test/activateAgreement.test.ts b/packages/agreement-process/test/activateAgreement.test.ts index 9a3b976b87..c3370b058c 100644 --- a/packages/agreement-process/test/activateAgreement.test.ts +++ b/packages/agreement-process/test/activateAgreement.test.ts @@ -892,16 +892,16 @@ describe("activate agreement", () => { const producer = getMockTenant(); const consumer = getMockTenant(); const authData = getRandomAuthData(); - const esevice = { + const eservice = { ...getMockEService(), producerId: producer.id, consumerId: consumer.id, descriptors: [getMockDescriptorPublished()], }; const agreement: Agreement = { - ...getMockAgreement(esevice.id), + ...getMockAgreement(eservice.id), state: agreementState.suspended, - descriptorId: esevice.descriptors[0].id, + descriptorId: eservice.descriptors[0].id, producerId: producer.id, consumerId: consumer.id, suspendedAt: new Date(), @@ -913,12 +913,13 @@ describe("activate agreement", () => { kind: delegationKind.delegatedProducer, eserviceId: agreement.eserviceId, delegateId: authData.organizationId, + delegatorId: eservice.producerId, state: delegationState.active, }); await addOneTenant(consumer); await addOneTenant(producer); - await addOneEService(esevice); + await addOneEService(eservice); await addOneAgreement(agreement); await addOneDelegation(delegation); diff --git a/packages/agreement-process/test/agreementConsumerDocuments.test.ts b/packages/agreement-process/test/agreementConsumerDocuments.test.ts index cfc060de1d..a37bf2c936 100644 --- a/packages/agreement-process/test/agreementConsumerDocuments.test.ts +++ b/packages/agreement-process/test/agreementConsumerDocuments.test.ts @@ -5,6 +5,7 @@ import { decodeProtobufPayload, getMockAgreement, getMockDelegation, + getMockDescriptorPublished, getMockEService, getMockTenant, getRandomAuthData, @@ -82,15 +83,27 @@ describe("agreement consumer document", () => { }); it("should succed when the requester is the delegate", async () => { - const eservice = getMockEService(); + const producer = getMockTenant(); + const consumer = getMockTenant(); + const authData = getRandomAuthData(); + const eservice = { + ...getMockEService(), + producerId: producer.id, + consumerId: consumer.id, + descriptors: [getMockDescriptorPublished()], + }; const agreement = { ...getMockAgreement(eservice.id), + descriptorId: eservice.descriptors[0].id, + producerId: producer.id, + consumerId: consumer.id, consumerDocuments: [generateMock(AgreementDocument)], }; const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, - delegateId: eservice.producerId, + delegateId: authData.organizationId, eserviceId: eservice.id, + delegatorId: eservice.producerId, state: delegationState.active, }); const delegate = getMockTenant(delegation.delegateId); @@ -100,7 +113,49 @@ describe("agreement consumer document", () => { await addOneAgreement(agreement); await addOneDelegation(delegation); - const authData = getRandomAuthData(eservice.producerId); + const result = await agreementService.getAgreementConsumerDocument( + agreement.id, + agreement.consumerDocuments[0].id, + { + authData, + serviceName: "", + correlationId: generateId(), + logger: genericLogger, + } + ); + + expect(result).toEqual(agreement.consumerDocuments[0]); + }); + + it("should succed when the requester is the producer, even if there is an active producer delegation", async () => { + const producer = getMockTenant(); + const consumer = getMockTenant(); + const authData = getRandomAuthData(producer.id); + const eservice = { + ...getMockEService(), + producerId: producer.id, + consumerId: consumer.id, + descriptors: [getMockDescriptorPublished()], + }; + const agreement = { + ...getMockAgreement(eservice.id), + descriptorId: eservice.descriptors[0].id, + producerId: producer.id, + consumerId: consumer.id, + consumerDocuments: [generateMock(AgreementDocument)], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: generateId(), + eserviceId: eservice.id, + delegatorId: eservice.producerId, + state: delegationState.active, + }); + + await addOneEService(eservice); + await addOneAgreement(agreement); + await addOneDelegation(delegation); + const result = await agreementService.getAgreementConsumerDocument( agreement.id, agreement.consumerDocuments[0].id, diff --git a/packages/agreement-process/test/rejectAgreement.test.ts b/packages/agreement-process/test/rejectAgreement.test.ts index 05c98d47d8..078a6867f0 100644 --- a/packages/agreement-process/test/rejectAgreement.test.ts +++ b/packages/agreement-process/test/rejectAgreement.test.ts @@ -195,6 +195,7 @@ describe("reject agreement", () => { kind: delegationKind.delegatedProducer, delegateId: authData.organizationId, eserviceId: eservice.id, + delegatorId: eservice.producerId, state: delegationState.active, }); if (type === "delegate") { diff --git a/packages/agreement-process/test/suspendAgreement.test.ts b/packages/agreement-process/test/suspendAgreement.test.ts index d815995581..d34cac56d2 100644 --- a/packages/agreement-process/test/suspendAgreement.test.ts +++ b/packages/agreement-process/test/suspendAgreement.test.ts @@ -439,6 +439,7 @@ describe("suspend agreement", () => { kind: delegationKind.delegatedProducer, delegateId: authData.organizationId, eserviceId: eservice.id, + delegatorId: eservice.producerId, state: delegationState.active, }); @@ -624,6 +625,7 @@ describe("suspend agreement", () => { kind: delegationKind.delegatedProducer, delegateId: delegate.id, eserviceId: eservice.id, + delegatorId: eservice.producerId, state: delegationState.active, }); @@ -662,6 +664,7 @@ describe("suspend agreement", () => { kind: delegationKind.delegatedProducer, delegateId: authData.organizationId, eserviceId: eservice.id, + delegatorId: eservice.producerId, state: delegationState.waitingForApproval, }); From 352c6a426cf5a2bfe1e9e37aa28fb77cd2c00840 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Wed, 4 Dec 2024 16:29:54 +0100 Subject: [PATCH 059/126] Removing unused components in API specs (#1244) --- packages/api-clients/open-api/bffApi.yml | 15 --- packages/api-clients/open-api/catalogApi.yml | 21 ---- .../api-clients/open-api/delegationApi.yml | 10 -- packages/api-clients/open-api/purposeApi.yml | 97 ------------------- 4 files changed, 143 deletions(-) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index f63b7604a6..d42758cb99 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -14338,21 +14338,6 @@ components: required: - results - pagination - ProductInfo: - type: object - additionalProperties: false - properties: - id: - type: string - role: - type: string - createdAt: - type: string - format: date-time - required: - - id - - role - - createdAt SelfcareProduct: type: object additionalProperties: false diff --git a/packages/api-clients/open-api/catalogApi.yml b/packages/api-clients/open-api/catalogApi.yml index 0a95620603..ce033ad875 100644 --- a/packages/api-clients/open-api/catalogApi.yml +++ b/packages/api-clients/open-api/catalogApi.yml @@ -1197,15 +1197,6 @@ components: schema: type: string schemas: - ResourceId: - type: object - additionalProperties: false - required: - - id - properties: - id: - type: string - format: uuid EServiceSeed: type: object additionalProperties: false @@ -1485,18 +1476,6 @@ components: enum: - AUTOMATIC - MANUAL - Organization: - type: object - additionalProperties: false - required: - - id - - name - properties: - id: - type: string - format: uuid - name: - type: string EServiceRiskAnalysisSeed: type: object additionalProperties: false diff --git a/packages/api-clients/open-api/delegationApi.yml b/packages/api-clients/open-api/delegationApi.yml index 88d3061ecc..a60bb3424c 100644 --- a/packages/api-clients/open-api/delegationApi.yml +++ b/packages/api-clients/open-api/delegationApi.yml @@ -421,16 +421,6 @@ components: required: - results - totalCount - CreatedResource: - type: object - additionalProperties: false - description: contains the id of the created resource - properties: - id: - type: string - format: uuid - required: - - id Delegation: type: object additionalProperties: false diff --git a/packages/api-clients/open-api/purposeApi.yml b/packages/api-clients/open-api/purposeApi.yml index 27df3fcd13..eef5a3b4e8 100644 --- a/packages/api-clients/open-api/purposeApi.yml +++ b/packages/api-clients/open-api/purposeApi.yml @@ -872,103 +872,6 @@ components: required: - id - value - Agreement: - type: object - additionalProperties: false - 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 - additionalProperties: false - properties: - id: - type: string - format: uuid - name: - type: string - required: - - id - - name - Clients: - type: array - items: - $ref: "#/components/schemas/Client" - EService: - type: object - additionalProperties: false - 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 - additionalProperties: false - 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 - additionalProperties: false - properties: - id: - type: string - format: uuid - name: - type: string - required: - - id - - name Purpose: type: object additionalProperties: false From 625ffa88cf1538eff98ab8eb79acf244520d310f Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 4 Dec 2024 16:50:40 +0100 Subject: [PATCH 060/126] Fix missing resources in delegation process build (#1251) --- packages/delegation-process/package.json | 3 ++- pnpm-lock.yaml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/delegation-process/package.json b/packages/delegation-process/package.json index 8b9da97bae..75c3a58051 100644 --- a/packages/delegation-process/package.json +++ b/packages/delegation-process/package.json @@ -12,7 +12,7 @@ "format:check": "prettier --check src", "format:write": "prettier --write src", "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", - "build": "tsc", + "build": "tsc && pnpm cpx './src/resources/**/*' './dist/resources'", "check": "tsc --project tsconfig.check.json" }, "keywords": [], @@ -22,6 +22,7 @@ "@anatine/zod-mock": "3.13.4", "@faker-js/faker": "8.4.1", "@pagopa/eslint-config": "3.0.0", + "cpx2": "7.0.1", "@protobuf-ts/runtime": "2.9.4", "@types/express": "4.17.21", "@types/node": "20.14.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eeed99ff12..1d77e41a03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1790,6 +1790,9 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 + cpx2: + specifier: 7.0.1 + version: 7.0.1 date-fns: specifier: 3.6.0 version: 3.6.0 From 4366702916d4ebe80b5df43815c215727eaf0e4c Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 5 Dec 2024 09:40:08 +0100 Subject: [PATCH 061/126] Fix Delegation bff api parsing (#1252) --- .../src/api/delegationApiConverter.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts index 0361703baf..95449a0521 100644 --- a/packages/backend-for-frontend/src/api/delegationApiConverter.ts +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -54,6 +54,18 @@ export function toDelegationKind( .exhaustive(); } +export function toBffDelegationApiDelegationDoc( + document: delegationApi.DelegationContractDocument +): bffApi.Document { + return { + id: document.id, + name: document.name, + contentType: document.contentType, + prettyName: document.prettyName, + createdAt: document.createdAt, + }; +} + export function toBffDelegationApiDelegation( delegation: delegationApi.Delegation, delegator: tenantApi.Tenant, @@ -79,8 +91,12 @@ export function toBffDelegationApiDelegation( id: delegator.id, name: delegator.name, }, - activationContract: delegation.activationContract, - revocationContract: delegation.revocationContract, + activationContract: delegation.activationContract + ? toBffDelegationApiDelegationDoc(delegation.activationContract) + : undefined, + revocationContract: delegation.revocationContract + ? toBffDelegationApiDelegationDoc(delegation.revocationContract) + : undefined, submittedAt: delegation.submittedAt, rejectionReason: delegation.rejectionReason, state: delegation.state, From d73a726d496c12284f2465c28f4acbb325210777 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 5 Dec 2024 10:53:40 +0100 Subject: [PATCH 062/126] Fix BFF e-service producer assertions (#1253) --- .../src/services/catalogService.ts | 21 ++++++++++---- .../src/services/validators.ts | 29 +++++++++++++++++-- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index 48926c86eb..231cd3d86c 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -61,6 +61,7 @@ import { getAllBulkAttributes } from "./attributeService.js"; import { assertNotDelegatedEservice, assertRequesterIsProducer, + assertRequesterCanActAsProducer, } from "./validators.js"; export type CatalogService = ReturnType; @@ -261,7 +262,12 @@ export function catalogServiceBuilder( headers, }); - assertRequesterIsProducer(requesterId, eservice); + await assertRequesterCanActAsProducer( + delegationProcessClient, + headers, + requesterId, + eservice + ); const descriptor = retrieveEserviceDescriptor(eservice, descriptorId); @@ -278,10 +284,10 @@ export function catalogServiceBuilder( descriptor ); - const requesterTenant = await tenantProcessClient.tenant.getTenant({ + const producerTenant = await tenantProcessClient.tenant.getTenant({ headers, params: { - id: requesterId, + id: eservice.producerId, }, }); @@ -302,7 +308,7 @@ export function catalogServiceBuilder( attributes: descriptorAttributes, eservice: toBffCatalogApiProducerDescriptorEService( eservice, - requesterTenant + producerTenant ), }; }, @@ -323,7 +329,12 @@ export function catalogServiceBuilder( headers, }); - assertRequesterIsProducer(requesterId, eservice); + await assertRequesterCanActAsProducer( + delegationProcessClient, + headers, + requesterId, + eservice + ); return { id: eservice.id, diff --git a/packages/backend-for-frontend/src/services/validators.ts b/packages/backend-for-frontend/src/services/validators.ts index 24f82f4820..8faa98b956 100644 --- a/packages/backend-for-frontend/src/services/validators.ts +++ b/packages/backend-for-frontend/src/services/validators.ts @@ -45,11 +45,36 @@ export function assertRequesterIsProducer( } } +export async function assertRequesterCanActAsProducer( + delegationProcessClient: DelegationProcessClient, + headers: BffAppContext["headers"], + requesterId: TenantId, + eservice: catalogApi.EService +): Promise { + try { + assertRequesterIsProducer(requesterId, eservice); + } catch { + const producerDelegations = await getAllDelegations( + delegationProcessClient, + headers, + { + kind: toDelegationKind(delegationKind.delegatedProducer), + delegateIds: [requesterId], + eserviceIds: [eservice.id], + delegationStates: [toDelegationState(delegationState.active)], + } + ); + if (producerDelegations.length === 0) { + throw invalidEServiceRequester(eservice.id, requesterId); + } + } +} + export async function assertNotDelegatedEservice( delegationProcessClient: DelegationProcessClient, headers: BffAppContext["headers"], delegatorId: TenantId, - eserviceid: EServiceId + eserviceId: EServiceId ): Promise { const delegations = await getAllDelegations( delegationProcessClient, @@ -57,7 +82,7 @@ export async function assertNotDelegatedEservice( { kind: toDelegationKind(delegationKind.delegatedProducer), delegatorIds: [delegatorId], - eserviceIds: [eserviceid], + eserviceIds: [eserviceId], delegationStates: [ toDelegationState(delegationState.active), toDelegationState(delegationState.waitingForApproval), From 5f9c0b840ca44dd90fd2bb0d5e51f586402368c5 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 5 Dec 2024 14:29:05 +0100 Subject: [PATCH 063/126] Fix delegation contract api zod parsing error (#1256) --- .../delegation-process/src/routers/DelegationRouter.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 9aed2fe580..9c136bc539 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -15,6 +15,7 @@ import { config } from "../config/config.js"; import { apiDelegationKindToDelegationKind, apiDelegationStateToDelegationState, + delegationContractToApiDelegationContract, delegationToApiDelegation, } from "../model/domain/apiConverter.js"; import { makeApiProblem } from "../model/domain/errors.js"; @@ -159,7 +160,11 @@ const delegationRouter = ( return res .status(200) - .send(delegationApi.DelegationContractDocument.parse(contract)); + .send( + delegationApi.DelegationContractDocument.parse( + delegationContractToApiDelegationContract(contract) + ) + ); } catch (error) { const errorRes = makeApiProblem( error, From 2a4f542bfdbf5142a87c74b8ce47dbf37504d1b1 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 5 Dec 2024 14:40:23 +0100 Subject: [PATCH 064/126] Add waiting for approval descriptors in BFF`draftDescriptors` property (#1255) --- packages/backend-for-frontend/src/api/catalogApiConverter.ts | 4 +++- packages/backend-for-frontend/src/services/catalogService.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/backend-for-frontend/src/api/catalogApiConverter.ts b/packages/backend-for-frontend/src/api/catalogApiConverter.ts index c01042454b..142b236a74 100644 --- a/packages/backend-for-frontend/src/api/catalogApiConverter.ts +++ b/packages/backend-for-frontend/src/api/catalogApiConverter.ts @@ -244,7 +244,9 @@ export function toBffCatalogApiProducerDescriptorEService( .map(toCompactDescriptor); const draftDescriptor = eservice.descriptors.find( - (d) => d.state === catalogApiDescriptorState.DRAFT + (d) => + d.state === catalogApiDescriptorState.DRAFT || + d.state === catalogApiDescriptorState.WAITING_FOR_APPROVAL ); return { diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index 231cd3d86c..bbfa4b4395 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -118,7 +118,9 @@ const enhanceProducerEService = ( ): bffApi.ProducerEService => { const activeDescriptor = getLatestActiveDescriptor(eservice); const draftDescriptor = eservice.descriptors.find( - (d) => d.state === catalogApiDescriptorState.DRAFT + (d) => + d.state === catalogApiDescriptorState.DRAFT || + d.state === catalogApiDescriptorState.WAITING_FOR_APPROVAL ); const isRequesterDelegateProducer = requesterId !== eservice.producerId; From 6f2b0a03bc0ca0937eaad4450d2f13be75eb9c17 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 6 Dec 2024 09:40:11 +0100 Subject: [PATCH 065/126] IMN-912 Refactor token generation states (#1184) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 18 +- .../src/consumerServiceV2.ts | 18 +- .../src/utils.ts | 54 +- .../consumerServiceV1.integration.test.ts | 926 ++++++----- .../consumerServiceV2.integration.test.ts | 1383 ++++++++++------- .../test/utils.test.ts | 308 ++-- .../src/consumerServiceV1.ts | 439 +++--- .../src/consumerServiceV2.ts | 362 +++-- .../src/utils.ts | 414 +++-- .../consumerServiceV1.integration.test.ts | 879 ++++++----- .../consumerServiceV2.integration.test.ts | 917 ++++++----- .../test/utils.test.ts | 479 +++--- .../src/model/domain/errors.ts | 38 +- .../src/services/tokenService.ts | 109 +- .../authorizationServer.integration.test.ts | 197 ++- .../test/authorizationServer.unit.test.ts | 101 +- .../src/services/toolService.ts | 7 +- .../catalog-platformstate-writer/src/utils.ts | 54 +- .../test/consumerServiceV1.test.ts | 304 ++-- .../test/consumerServiceV2.test.ts | 784 ++++++---- .../test/utils.test.ts | 287 ++-- .../client-assertion-validation/src/errors.ts | 41 +- .../client-assertion-validation/src/utils.ts | 24 +- .../src/validation.ts | 38 +- .../test/utils.test.ts | 52 +- .../test/validation.test.ts | 167 +- packages/commons-test/src/testUtils.ts | 112 +- .../src/tokenGenerationReadmodelUtils.ts | 160 +- packages/models/src/brandedIds.ts | 21 +- packages/models/src/index.ts | 1 + .../src/token-generation-readmodel/commons.ts | 10 +- .../platform-states-entry.ts | 4 +- .../token-generation-states-entry.ts | 39 +- .../src/consumerServiceV1.ts | 12 +- .../src/consumerServiceV2.ts | 10 +- .../purpose-platformstate-writer/src/utils.ts | 280 ++-- .../consumerServiceV1.integration.test.ts | 449 +++--- .../consumerServiceV2.integration.test.ts | 1113 ++++++++----- .../test/utils.test.ts | 551 ++++--- .../test/utils.ts | 16 +- 40 files changed, 6314 insertions(+), 4864 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index f557ec080f..7e69f31097 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -18,10 +18,10 @@ import { readAgreementEntry, updateAgreementStateInPlatformStatesEntry, agreementStateToItemState, - updateAgreementStateOnTokenStates, + updateAgreementStateOnTokenGenStates, writeAgreementEntry, readCatalogEntry, - updateAgreementStateAndDescriptorInfoOnTokenStates, + updateAgreementStateAndDescriptorInfoOnTokenGenStates, deleteAgreementEntry, isLatestAgreement, } from "./utils.js"; @@ -174,7 +174,7 @@ const handleFirstActivation = async ( }); // token-generation-states - await updateAgreementStateAndDescriptorInfoOnTokenStates({ + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId: agreement.id, agreementState: agreement.state, @@ -234,7 +234,7 @@ const handleActivationOrSuspension = async ( ) ) { // token-generation-states - await updateAgreementStateAndDescriptorInfoOnTokenStates({ + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId: agreement.id, agreementState: agreement.state, @@ -264,7 +264,7 @@ const handleArchiving = async ( ) { // token-generation-states only if agreement is the latest - await updateAgreementStateOnTokenStates({ + await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreement.state, dynamoDBClient, @@ -320,7 +320,7 @@ const handleUpgrade = async ( await writeAgreementEntry(newAgreementEntry, dynamoDBClient); } - const updateLatestAgreementOnTokenStates = async ( + const updateLatestAgreementOnTokenGenStates = async ( catalogEntry: PlatformStatesCatalogEntry | undefined ): Promise => { if ( @@ -336,7 +336,7 @@ const handleUpgrade = async ( descriptorId: agreement.descriptorId, }); - await updateAgreementStateAndDescriptorInfoOnTokenStates({ + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId: agreement.id, agreementState: agreement.state, @@ -347,7 +347,7 @@ const handleUpgrade = async ( } }; - await updateLatestAgreementOnTokenStates(catalogEntry); + await updateLatestAgreementOnTokenGenStates(catalogEntry); const secondRetrievalCatalogEntry = await readCatalogEntry( pkCatalogEntry, @@ -357,6 +357,6 @@ const handleUpgrade = async ( secondRetrievalCatalogEntry && (!catalogEntry || secondRetrievalCatalogEntry.state !== catalogEntry.state) ) { - await updateLatestAgreementOnTokenStates(secondRetrievalCatalogEntry); + await updateLatestAgreementOnTokenGenStates(secondRetrievalCatalogEntry); } }; diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index 0edf4c84fc..e7698d371b 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -19,8 +19,8 @@ import { readAgreementEntry, readCatalogEntry, updateAgreementStateInPlatformStatesEntry, - updateAgreementStateOnTokenStates, - updateAgreementStateAndDescriptorInfoOnTokenStates, + updateAgreementStateOnTokenGenStates, + updateAgreementStateAndDescriptorInfoOnTokenGenStates, writeAgreementEntry, isLatestAgreement, } from "./utils.js"; @@ -97,7 +97,7 @@ export async function handleMessageV2( }); // token-generation-states - await updateAgreementStateAndDescriptorInfoOnTokenStates({ + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId: agreement.id, agreementState: agreement.state, @@ -146,7 +146,7 @@ export async function handleMessageV2( ) { // token-generation-states only if agreement is the latest - await updateAgreementStateOnTokenStates({ + await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreement.state, dynamoDBClient, @@ -204,7 +204,7 @@ export async function handleMessageV2( await writeAgreementEntry(newAgreementEntry, dynamoDBClient); } - const updateLatestAgreementOnTokenStates = async ( + const updateLatestAgreementOnTokenGenStates = async ( catalogEntry: PlatformStatesCatalogEntry | undefined ): Promise => { if ( @@ -222,7 +222,7 @@ export async function handleMessageV2( } ); - await updateAgreementStateAndDescriptorInfoOnTokenStates({ + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId: agreement.id, agreementState: agreement.state, @@ -238,7 +238,7 @@ export async function handleMessageV2( dynamoDBClient ); - await updateLatestAgreementOnTokenStates(catalogEntry); + await updateLatestAgreementOnTokenGenStates(catalogEntry); const updatedCatalogEntry = await readCatalogEntry( pkCatalogEntry, @@ -249,7 +249,7 @@ export async function handleMessageV2( updatedCatalogEntry && (!catalogEntry || updatedCatalogEntry.state !== catalogEntry.state) ) { - await updateLatestAgreementOnTokenStates(updatedCatalogEntry); + await updateLatestAgreementOnTokenGenStates(updatedCatalogEntry); } }) .with({ type: "AgreementArchivedByUpgrade" }, async (msg) => { @@ -275,7 +275,7 @@ export async function handleMessageV2( ) { // token-generation-states only if agreement is the latest - await updateAgreementStateOnTokenStates({ + await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreement.state, dynamoDBClient, diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index 11af0e0e28..d669123ae8 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -11,7 +11,7 @@ import { PlatformStatesAgreementPK, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { AttributeValue, @@ -151,12 +151,12 @@ export const updateAgreementStateInPlatformStatesEntry = async ( export const agreementStateToItemState = (state: AgreementState): ItemState => state === agreementState.active ? itemState.active : itemState.inactive; -export const updateAgreementStateOnTokenStatesEntries = async ({ +export const updateAgreementStateOnTokenGenStatesEntries = async ({ entriesToUpdate, agreementState, dynamoDBClient, }: { - entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + entriesToUpdate: TokenGenerationStatesConsumerClient[]; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; }): Promise => { @@ -187,7 +187,7 @@ export const updateAgreementStateOnTokenStatesEntries = async ({ } }; -export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = +export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = async ({ entriesToUpdate, agreementId, @@ -196,7 +196,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStatesEntries = GSIPK_eserviceId_descriptorId, catalogEntry, }: { - entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + entriesToUpdate: TokenGenerationStatesConsumerClient[]; agreementId: AgreementId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; @@ -328,7 +328,7 @@ export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( ); }; -export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ +export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ GSIPK_consumerId_eserviceId, agreementId, agreementState, @@ -342,12 +342,12 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ dynamoDBClient: DynamoDBClient; GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; catalogEntry: PlatformStatesCatalogEntry | undefined; -}): Promise => { +}): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Agreement", @@ -367,20 +367,20 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } - await updateAgreementStateAndDescriptorInfoOnTokenStatesEntries({ - entriesToUpdate: tokenStateEntries.data, + await updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries({ + entriesToUpdate: tokenGenStatesEntries.data, agreementId, agreementState, dynamoDBClient, @@ -389,10 +389,10 @@ export const updateAgreementStateAndDescriptorInfoOnTokenStates = async ({ }); if (!data.LastEvaluatedKey) { - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } else { return [ - ...tokenStateEntries.data, + ...tokenGenStatesEntries.data, ...(await runPaginatedQuery( consumerId_eserviceId, dynamoDBClient, @@ -427,7 +427,7 @@ export const extractAgreementIdFromAgreementPK = ( return result.data; }; -export const updateAgreementStateOnTokenStates = async ({ +export const updateAgreementStateOnTokenGenStates = async ({ GSIPK_consumerId_eserviceId, agreementState, dynamoDBClient, @@ -435,12 +435,12 @@ export const updateAgreementStateOnTokenStates = async ({ GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; -}): Promise => { +}): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Agreement", @@ -460,29 +460,29 @@ export const updateAgreementStateOnTokenStates = async ({ } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } - await updateAgreementStateOnTokenStatesEntries({ - entriesToUpdate: tokenStateEntries.data, + await updateAgreementStateOnTokenGenStatesEntries({ + entriesToUpdate: tokenGenStatesEntries.data, agreementState, dynamoDBClient, }); if (!data.LastEvaluatedKey) { - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } else { return [ - ...tokenStateEntries.data, + ...tokenGenStatesEntries.data, ...(await runPaginatedQuery( consumerId_eserviceId, dynamoDBClient, diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 0ce46ae4e3..230c36c68f 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -21,7 +21,7 @@ import { PlatformStatesAgreementEntry, PlatformStatesCatalogEntry, TenantId, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, agreementState, generateId, itemState, @@ -33,15 +33,15 @@ import { } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, getMockAgreement, buildDynamoDBTables, deleteDynamoDBTables, toAgreementV1, - readTokenStateEntriesByConsumerIdEserviceId, - getMockAgreementEntry, - writeCatalogEntry, - writeTokenStateEntry, + readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId, + getMockPlatformStatesAgreementEntry, + writePlatformCatalogEntry, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; @@ -97,41 +97,49 @@ describe("integration tests V1 events", async () => { agreement.id ); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; await writeAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -144,17 +152,17 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -185,43 +193,51 @@ describe("integration tests V1 events", async () => { agreement.id ); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; await writeAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -243,33 +259,33 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -301,37 +317,45 @@ describe("integration tests V1 events", async () => { ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -361,33 +385,33 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementId: agreement.id, GSIPK_eserviceId_descriptorId, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -432,44 +456,52 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -499,14 +531,14 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementId: agreement.id, agreementState: itemState.active, descriptorState: catalogEntry.state, @@ -515,9 +547,9 @@ describe("integration tests V1 events", async () => { GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementId: agreement.id, agreementState: itemState.active, descriptorState: catalogEntry.state, @@ -527,11 +559,11 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -596,7 +628,7 @@ describe("integration tests V1 events", async () => { }); const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -614,40 +646,48 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -673,16 +713,16 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -715,7 +755,7 @@ describe("integration tests V1 events", async () => { agreement.id ); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 3, }; await writeAgreementEntry(previousStateEntry, dynamoDBClient); @@ -733,38 +773,46 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -776,17 +824,17 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -844,7 +892,9 @@ describe("integration tests V1 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -852,7 +902,7 @@ describe("integration tests V1 events", async () => { previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -875,38 +925,46 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -927,34 +985,34 @@ describe("integration tests V1 events", async () => { eserviceId: latestAgreement.eserviceId, descriptorId: latestAgreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1012,7 +1070,9 @@ describe("integration tests V1 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -1020,7 +1080,7 @@ describe("integration tests V1 events", async () => { previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -1043,36 +1103,44 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -1089,17 +1157,17 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -1142,21 +1210,22 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ @@ -1164,16 +1233,20 @@ describe("integration tests V1 events", async () => { descriptorId: generateId(), }), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ @@ -1181,7 +1254,10 @@ describe("integration tests V1 events", async () => { descriptorId: generateId(), }), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -1211,33 +1287,33 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1335,7 +1411,9 @@ describe("integration tests V1 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1343,7 +1421,7 @@ describe("integration tests V1 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1354,33 +1432,41 @@ describe("integration tests V1 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -1397,17 +1483,17 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1466,7 +1552,9 @@ describe("integration tests V1 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1474,7 +1562,7 @@ describe("integration tests V1 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1485,14 +1573,15 @@ describe("integration tests V1 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, @@ -1501,16 +1590,20 @@ describe("integration tests V1 events", async () => { descriptorId: latestAgreement.descriptorId, }), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, @@ -1519,7 +1612,10 @@ describe("integration tests V1 events", async () => { descriptorId: latestAgreement.descriptorId, }), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -1536,31 +1632,31 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementId: latestAgreement.id, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementId: latestAgreement.id, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1658,7 +1754,9 @@ describe("integration tests V1 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1666,7 +1764,7 @@ describe("integration tests V1 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1677,33 +1775,41 @@ describe("integration tests V1 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -1720,17 +1826,17 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1789,7 +1895,9 @@ describe("integration tests V1 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1797,7 +1905,7 @@ describe("integration tests V1 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1808,14 +1916,15 @@ describe("integration tests V1 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -1824,16 +1933,20 @@ describe("integration tests V1 events", async () => { descriptorId: latestAgreement.descriptorId, }), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -1842,7 +1955,10 @@ describe("integration tests V1 events", async () => { descriptorId: latestAgreement.descriptorId, }), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -1859,31 +1975,31 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1948,14 +2064,16 @@ describe("integration tests V1 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1965,33 +2083,41 @@ describe("integration tests V1 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -2002,29 +2128,29 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2082,14 +2208,16 @@ describe("integration tests V1 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -2099,33 +2227,41 @@ describe("integration tests V1 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -2136,17 +2272,17 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index 08186fc1cd..d6b41f15b2 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -27,7 +27,7 @@ import { PlatformStatesAgreementEntry, PlatformStatesCatalogEntry, TenantId, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, agreementState, generateId, itemState, @@ -40,14 +40,14 @@ import { } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, getMockAgreement, buildDynamoDBTables, deleteDynamoDBTables, - readTokenStateEntriesByConsumerIdEserviceId, - writeTokenStateEntry, - getMockAgreementEntry, - writeCatalogEntry, + readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId, + writeTokenGenStatesConsumerClient, + getMockPlatformStatesAgreementEntry, + writePlatformCatalogEntry, } from "pagopa-interop-commons-test"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; @@ -103,41 +103,49 @@ describe("integration tests V2 events", async () => { agreement.id ); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; await writeAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -150,17 +158,17 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -191,42 +199,51 @@ describe("integration tests V2 events", async () => { agreement.id ); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; await writeAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -248,33 +265,33 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementId: agreement.id, agreementState: itemState.active, GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementId: agreement.id, agreementState: itemState.active, GSIPK_eserviceId_descriptorId, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -306,37 +323,45 @@ describe("integration tests V2 events", async () => { ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -366,33 +391,33 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -437,44 +462,52 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -504,14 +537,14 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, @@ -520,9 +553,9 @@ describe("integration tests V2 events", async () => { descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, agreementState: itemState.active, @@ -532,11 +565,11 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -602,7 +635,7 @@ describe("integration tests V2 events", async () => { }); const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -620,40 +653,48 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: undefined, descriptorAudience: undefined, descriptorState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -679,16 +720,16 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -785,7 +826,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -793,7 +836,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -804,33 +847,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -847,17 +898,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -916,7 +967,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -924,7 +977,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -935,33 +988,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -978,29 +1039,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1097,7 +1158,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1105,7 +1168,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1116,33 +1179,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1159,17 +1230,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1228,7 +1299,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1236,7 +1309,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1247,33 +1320,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1290,29 +1371,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1409,7 +1490,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1417,7 +1500,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1428,33 +1511,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1471,17 +1562,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1540,7 +1631,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1548,7 +1641,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1559,33 +1652,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1602,29 +1703,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1721,7 +1822,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1729,7 +1832,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1740,33 +1843,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1783,17 +1894,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1852,7 +1963,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1860,7 +1973,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -1871,33 +1984,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1914,29 +2035,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -2034,7 +2155,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -2042,7 +2165,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -2053,33 +2176,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2096,17 +2227,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -2165,7 +2296,9 @@ describe("integration tests V2 events", async () => { }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -2173,7 +2306,7 @@ describe("integration tests V2 events", async () => { }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -2184,33 +2317,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2227,29 +2368,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -2283,7 +2424,7 @@ describe("integration tests V2 events", async () => { agreement.id ); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 3, }; await writeAgreementEntry(previousStateEntry, dynamoDBClient); @@ -2301,38 +2442,46 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2344,17 +2493,17 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -2412,7 +2561,9 @@ describe("integration tests V2 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -2420,7 +2571,7 @@ describe("integration tests V2 events", async () => { previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -2443,38 +2594,46 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_eserviceId_descriptorId: undefined, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_eserviceId_descriptorId: undefined, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2495,34 +2654,34 @@ describe("integration tests V2 events", async () => { eserviceId, descriptorId: latestAgreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: latestAgreement.id, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2580,7 +2739,9 @@ describe("integration tests V2 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -2588,7 +2749,7 @@ describe("integration tests V2 events", async () => { previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), version: 1, state: itemState.inactive, GSIPK_consumerId_eserviceId, @@ -2611,36 +2772,44 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2657,17 +2826,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -2710,21 +2879,22 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.active, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ @@ -2732,16 +2902,20 @@ describe("integration tests V2 events", async () => { descriptorId: generateId(), }), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.active, GSIPK_consumerId_eserviceId, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ @@ -2749,7 +2923,10 @@ describe("integration tests V2 events", async () => { descriptorId: generateId(), }), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2779,31 +2956,31 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2852,7 +3029,7 @@ describe("integration tests V2 events", async () => { eserviceId, }); const agreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), state: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2860,33 +3037,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(agreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -2897,17 +3082,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -2968,14 +3153,16 @@ describe("integration tests V2 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -2985,33 +3172,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -3022,29 +3217,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -3102,14 +3297,16 @@ describe("integration tests V2 events", async () => { eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(previousAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: previousAgreement.stamps.activation!.when.toISOString(), }; const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(latestAgreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: @@ -3119,33 +3316,41 @@ describe("integration tests V2 events", async () => { await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementId: previousAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -3156,17 +3361,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index edd0a33ec7..9f9c8ee651 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -8,11 +8,11 @@ import { import { buildDynamoDBTables, deleteDynamoDBTables, - getMockAgreementEntry, - getMockTokenStatesClientPurposeEntry, - readAllTokenStateItems, - readTokenStateEntriesByConsumerIdEserviceId, - writeTokenStateEntry, + getMockPlatformStatesAgreementEntry, + getMockTokenGenStatesConsumerClient, + readAllTokenGenStatesItems, + readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { makePlatformStatesAgreementPK, @@ -25,11 +25,11 @@ import { makePlatformStatesEServiceDescriptorPK, agreementState, makeTokenGenerationStatesClientKidPurposePK, - TokenGenerationStatesClientPurposeEntry, makeGSIPKEServiceIdDescriptorId, EServiceId, PlatformStatesCatalogEntry, TenantId, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { afterAll, @@ -47,8 +47,8 @@ import { writeAgreementEntry, deleteAgreementEntry, agreementStateToItemState, - updateAgreementStateOnTokenStates, - updateAgreementStateAndDescriptorInfoOnTokenStates, + updateAgreementStateOnTokenGenStates, + updateAgreementStateAndDescriptorInfoOnTokenGenStates, isLatestAgreement, } from "../src/utils.js"; import { config } from "./utils.js"; @@ -98,7 +98,8 @@ describe("utils", async () => { const primaryKey = makePlatformStatesAgreementPK( generateId() ); - const previousAgreementStateEntry = getMockAgreementEntry(primaryKey); + const previousAgreementStateEntry = + getMockPlatformStatesAgreementEntry(primaryKey); expect( await readAgreementEntry(primaryKey, dynamoDBClient) ).toBeUndefined(); @@ -127,7 +128,8 @@ describe("utils", async () => { const primaryKey = makePlatformStatesAgreementPK( generateId() ); - const agreementStateEntry = getMockAgreementEntry(primaryKey); + const agreementStateEntry = + getMockPlatformStatesAgreementEntry(primaryKey); await writeAgreementEntry(agreementStateEntry, dynamoDBClient); expect( writeAgreementEntry(agreementStateEntry, dynamoDBClient) @@ -255,13 +257,13 @@ describe("utils", async () => { ); }); - describe("readTokenStateEntriesByConsumerIdEserviceId", async () => { + describe("readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId", async () => { it("should return empty array if entries do not exist", async () => { const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: generateId(), eserviceId: generateId(), }); - const result = await readTokenStateEntriesByConsumerIdEserviceId( + const result = await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); @@ -269,44 +271,57 @@ describe("utils", async () => { }); it("should return entries if they exist (no need for pagination)", async () => { - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: generateId(), eserviceId: generateId(), }); - const tokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_consumerId_eserviceId, - }; - await writeTokenStateEntry(tokenStateEntry1, dynamoDBClient); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_consumerId_eserviceId, - }; - await writeTokenStateEntry(tokenStateEntry2, dynamoDBClient); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); - const retrievedTokenEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - expect(retrievedTokenEntries).toEqual( - expect.arrayContaining([tokenStateEntry1, tokenStateEntry2]) + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, + ]) ); }); @@ -318,124 +333,143 @@ describe("utils", async () => { const tokenEntriesLength = 10; - const writtenEntries = []; + const writtenTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = + []; // eslint-disable-next-line functional/no-let for (let i = 0; i < tokenEntriesLength; i++) { - const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_consumerId_eserviceId, - publicKey: crypto.randomBytes(100000).toString("hex"), - }; - await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + const tokenGenStatesEntryPK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_consumerId_eserviceId, + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); // eslint-disable-next-line functional/immutable-data - writtenEntries.push(tokenStateEntry); + writtenTokenGenStatesConsumerClients.push(tokenGenStatesConsumerClient); } vi.spyOn(dynamoDBClient, "send"); - const tokenEntries = await readTokenStateEntriesByConsumerIdEserviceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const tokenGenStatesConsumerClients = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( + GSIPK_consumerId_eserviceId, + dynamoDBClient + ); expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); - expect(tokenEntries).toHaveLength(tokenEntriesLength); - expect(tokenEntries).toEqual(expect.arrayContaining(writtenEntries)); + expect(tokenGenStatesConsumerClients).toHaveLength(tokenEntriesLength); + expect(tokenGenStatesConsumerClients).toEqual( + expect.arrayContaining(writtenTokenGenStatesConsumerClients) + ); }); }); - describe("updateAgreementStateOnTokenStates", async () => { + describe("updateAgreementStateOnTokenGenStates", async () => { it("should do nothing if previous entry doesn't exist", async () => { const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: generateId(), eserviceId: generateId(), }); - const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); - expect(tokenStateEntries).toEqual([]); + const tokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + expect(tokenGenStatesEntries).toEqual([]); expect( - updateAgreementStateOnTokenStates({ + updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreementState.archived, dynamoDBClient, }) ).resolves.not.toThrowError(); - const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(tokenStateEntriesAfterUpdate).toEqual([]); + expect(tokenGenStatesEntriesAfterUpdate).toEqual([]); }); it("should update state if previous entries exist", async () => { - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: generateId(), eserviceId: generateId(), }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], GSIPK_consumerId_eserviceId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await updateAgreementStateOnTokenStates({ + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreementState.active, dynamoDBClient, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); }); - describe("updateAgreementStateAndDescriptorInfoOnTokenStates", async () => { + describe("updateAgreementStateAndDescriptorInfoOnTokenGenStates", async () => { it("should do nothing if previous entry doesn't exist", async () => { const agreementId = generateId(); const eserviceId = generateId(); @@ -464,10 +498,12 @@ describe("utils", async () => { updatedAt: new Date().toISOString(), }; - const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); - expect(tokenStateEntries).toEqual([]); + const tokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + expect(tokenGenStatesEntries).toEqual([]); expect( - updateAgreementStateAndDescriptorInfoOnTokenStates({ + updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId, agreementState: agreementState.archived, @@ -476,10 +512,10 @@ describe("utils", async () => { catalogEntry, }) ).resolves.not.toThrowError(); - const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(tokenStateEntriesAfterUpdate).toEqual([]); + expect(tokenGenStatesEntriesAfterUpdate).toEqual([]); }); it("should update state if previous entries exist", async () => { @@ -509,38 +545,46 @@ describe("utils", async () => { version: 3, updatedAt: new Date().toISOString(), }; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, descriptorState: undefined, descriptorAudience: [], descriptorVoucherLifespan: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, descriptorState: undefined, descriptorAudience: [], descriptorVoucherLifespan: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); - await updateAgreementStateAndDescriptorInfoOnTokenStates({ + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementId, agreementState: agreementState.active, @@ -548,14 +592,14 @@ describe("utils", async () => { GSIPK_eserviceId_descriptorId, catalogEntry, }); - const retrievedTokenStateEntries = - await readTokenStateEntriesByConsumerIdEserviceId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( GSIPK_consumerId_eserviceId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId, agreementState: itemState.active, @@ -564,9 +608,9 @@ describe("utils", async () => { descriptorAudience: catalogEntry.descriptorAudience, descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId, agreementState: itemState.active, @@ -576,11 +620,11 @@ describe("utils", async () => { descriptorVoucherLifespan: catalogEntry.descriptorVoucherLifespan, }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -605,13 +649,19 @@ describe("utils", async () => { const agreementPK2 = makePlatformStatesAgreementPK(agreementId2); const agreementEntry1: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementPK1, GSIPK_consumerId_eserviceId), + ...getMockPlatformStatesAgreementEntry( + agreementPK1, + GSIPK_consumerId_eserviceId + ), GSISK_agreementTimestamp: now.toISOString(), state: itemState.active, }; const agreementEntry2: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementPK2, GSIPK_consumerId_eserviceId), + ...getMockPlatformStatesAgreementEntry( + agreementPK2, + GSIPK_consumerId_eserviceId + ), GSISK_agreementTimestamp: threeHoursAgo.toISOString(), state: itemState.inactive, }; diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index bc0af7806f..4f1b9fd453 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -1,12 +1,12 @@ -import { match } from "ts-pattern"; +import { match, P } from "ts-pattern"; import { AuthorizationEventEnvelopeV1, Client, ClientId, + clientKindTokenGenStates, ClientV1, fromClientV1, fromKeyV1, - genericInternalError, itemState, Key, KeyV1, @@ -20,30 +20,30 @@ import { missingKafkaMessageDataError, PlatformStatesClientEntry, PurposeId, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, unsafeBrandId, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { clientKindToTokenGenerationStatesClientKind, convertEntriesToClientKidInTokenGenerationStates, - createTokenClientPurposeEntry, + createTokenGenStatesConsumerClient, deleteClientEntryFromPlatformStates, - deleteClientEntryFromTokenGenerationStatesTable, - deleteEntriesFromTokenStatesByClient, - deleteEntriesFromTokenStatesByKid, - deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, + deleteClientEntryFromTokenGenerationStates, + deleteEntriesFromTokenGenStatesByClientId, + deleteEntriesFromTokenGenStatesByKid, + deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, extractAgreementIdFromAgreementPK, - extractKidFromTokenEntryPK, - readClientEntriesInTokenGenerationStates, - readClientEntry, + extractKidFromTokenGenStatesEntryPK, + readConsumerClientEntriesInTokenGenerationStates, + readPlatformClientEntry, retrievePlatformStatesByPurpose, setClientPurposeIdsInPlatformStatesEntry, - updateTokenDataForSecondRetrieval, + updateTokenGenStatesDataForSecondRetrieval, upsertPlatformClientEntry, - upsertTokenClientKidEntry, - upsertTokenStateClientPurposeEntry, + upsertTokenGenStatesApiClient, + upsertTokenGenStatesConsumerClient, } from "./utils.js"; export async function handleMessageV1( @@ -55,7 +55,7 @@ export async function handleMessageV1( const client = parseClient(msg.data.client, msg.type); const pk = makePlatformStatesClientPK(client.id); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (clientEntry && clientEntry.version > msg.version) { return Promise.resolve(); @@ -72,6 +72,7 @@ export async function handleMessageV1( await upsertPlatformClientEntry(updatedClientEntry, dynamoDBClient); } }) + // eslint-disable-next-line sonarjs/cognitive-complexity .with({ type: "KeysAdded" }, async (msg) => { const keyV1 = msg.data.keys[0].value; @@ -81,7 +82,7 @@ export async function handleMessageV1( const pem = key.encodedPem; const pk = makePlatformStatesClientPK(clientId); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (!clientEntry || clientEntry.version > msg.version) { return Promise.resolve(); @@ -99,118 +100,148 @@ export async function handleMessageV1( }; await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); - if (clientPurposesIds.length > 0) { - const addedEntries = await Promise.all( - clientPurposesIds.map(async (purposeId) => { - const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient - ); - - const tokenClientKidPurposePK = - makeTokenGenerationStatesClientKidPurposePK({ - clientId, - kid, - purposeId, - }); + await match(clientEntry.clientKind) + .with(clientKindTokenGenStates.consumer, async () => { + if (clientPurposesIds.length > 0) { + const addedEntries = await Promise.all( + clientPurposesIds.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenClientKidPurposePK, + consumerId: platformClientEntry.clientConsumerId, + clientKind: clientKindTokenGenStates.consumer, + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...(purposeEntry + ? { + GSIPK_consumerId_eserviceId: + makeGSIPKConsumerIdEServiceId({ + consumerId: + platformClientEntry.clientConsumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + } + : {}), + ...(purposeEntry && agreementEntry + ? { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: + agreementEntry.agreementDescriptorId, + }), + } + : {}), + ...(catalogEntry + ? { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + } + : {}), + }; + await upsertTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + return tokenGenStatesConsumerClient; + }) + ); - const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + // Second check for updated fields + await Promise.all( + clientPurposesIds.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const addedTokenGenStatesConsumerClient = addedEntries[index]; + await updateTokenGenStatesDataForSecondRetrieval({ + dynamoDBClient, + entry: addedTokenGenStatesConsumerClient, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } else { + const tokenGenStatesConsumerClientWithoutPurpose: TokenGenerationStatesConsumerClient = { - PK: tokenClientKidPurposePK, + PK: makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }), consumerId: platformClientEntry.clientConsumerId, - clientKind: platformClientEntry.clientKind, + clientKind: clientKindTokenGenStates.consumer, publicKey: pem, - updatedAt: new Date().toISOString(), GSIPK_clientId: clientId, GSIPK_kid: makeGSIPKKid(kid), - GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ - clientId, - purposeId, - }), - GSIPK_purposeId: purposeId, - ...((purposeEntry && { - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: platformClientEntry.clientConsumerId, - eserviceId: purposeEntry.purposeEserviceId, - }), - purposeState: purposeEntry.state, - purposeVersionId: purposeEntry.purposeVersionId, - }) || - {}), - ...((purposeEntry && - agreementEntry && { - agreementId: extractAgreementIdFromAgreementPK( - agreementEntry.PK - ), - agreementState: agreementEntry.state, - GSIPK_eserviceId_descriptorId: - makeGSIPKEServiceIdDescriptorId({ - eserviceId: purposeEntry.purposeEserviceId, - descriptorId: agreementEntry.agreementDescriptorId, - }), - }) || - {}), - ...((catalogEntry && { - descriptorState: catalogEntry.state, - descriptorAudience: catalogEntry.descriptorAudience, - descriptorVoucherLifespan: - catalogEntry.descriptorVoucherLifespan, - }) || - {}), + updatedAt: new Date().toISOString(), }; - - await upsertTokenStateClientPurposeEntry( - clientKidPurposeEntry, + await upsertTokenGenStatesConsumerClient( + tokenGenStatesConsumerClientWithoutPurpose, dynamoDBClient ); - return clientKidPurposeEntry; - }) - ); - - // Second check for updated fields - await Promise.all( - clientPurposesIds.map(async (purposeId, index) => { - const { - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - } = await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient - ); - - const addedClientKidPurposeEntry = addedEntries[index]; - await updateTokenDataForSecondRetrieval({ - dynamoDBClient, - entry: addedClientKidPurposeEntry, - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - }); - }) - ); - } else { - const clientKidEntry: TokenGenerationStatesClientEntry = { - PK: makeTokenGenerationStatesClientKidPK({ - clientId, - kid, - }), - consumerId: platformClientEntry.clientConsumerId, - clientKind: platformClientEntry.clientKind, - publicKey: pem, - GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), - updatedAt: new Date().toISOString(), - }; - await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); - } + } + }) + .with(clientKindTokenGenStates.api, async () => { + const tokenGenStatesApiClient: TokenGenerationStatesApiClient = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }), + consumerId: platformClientEntry.clientConsumerId, + clientKind: clientKindTokenGenStates.api, + publicKey: pem, + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + updatedAt: new Date().toISOString(), + }; + await upsertTokenGenStatesApiClient( + tokenGenStatesApiClient, + dynamoDBClient + ); + }) + .exhaustive(); } }) .with({ type: "KeyDeleted" }, async (msg) => { const clientId = unsafeBrandId(msg.data.clientId); const pk = makePlatformStatesClientPK(clientId); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (!clientEntry || clientEntry.version > msg.version) { return Promise.resolve(); @@ -227,11 +258,12 @@ export async function handleMessageV1( await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); const GSIPK_kid = makeGSIPKKid(msg.data.keyId); - await deleteEntriesFromTokenStatesByKid(GSIPK_kid, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByKid(GSIPK_kid, dynamoDBClient); } }) .with({ type: "ClientPurposeAdded" }, async (msg) => { const clientId = unsafeBrandId(msg.data.clientId); + const unparsedPurposeId = msg.data.statesChain?.purpose?.purposeId; if (!unparsedPurposeId) { @@ -240,7 +272,7 @@ export async function handleMessageV1( const purposeId = unsafeBrandId(unparsedPurposeId); const pk = makePlatformStatesClientPK(clientId); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (!clientEntry || clientEntry.version > msg.version) { return Promise.resolve(); } else { @@ -253,101 +285,104 @@ export async function handleMessageV1( }, dynamoDBClient ); - } - - const GSIPK_clientId = clientId; - const tokenClientEntries = await readClientEntriesInTokenGenerationStates( - GSIPK_clientId, - dynamoDBClient - ); - if (tokenClientEntries.length === 0) { - return Promise.resolve(); - } else { - const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); - - const seenKids = new Set(); - const addedTokenClientPurposeEntries = await Promise.all( - tokenClientEntries.map(async (entry) => { - const parsedTokenClientEntry = - TokenGenerationStatesClientEntry.safeParse(entry); - const parsedTokenClientPurposeEntry = - TokenGenerationStatesClientPurposeEntry.safeParse(entry); - - if (parsedTokenClientEntry.success) { - const newTokenClientPurposeEntry = createTokenClientPurposeEntry({ - tokenEntry: parsedTokenClientEntry.data, - kid: extractKidFromTokenEntryPK(parsedTokenClientEntry.data.PK), - clientId, - consumerId: parsedTokenClientEntry.data.consumerId, - purposeId, - purposeEntry, - agreementEntry, - catalogEntry, - }); - - await upsertTokenStateClientPurposeEntry( - newTokenClientPurposeEntry, - dynamoDBClient - ); - await deleteClientEntryFromTokenGenerationStatesTable( - entry, - dynamoDBClient - ); - return newTokenClientPurposeEntry; - } - if (parsedTokenClientPurposeEntry.success) { - const kid = extractKidFromTokenEntryPK( - parsedTokenClientPurposeEntry.data.PK - ); - if (!seenKids.has(kid)) { - const newTokenClientPurposeEntry = - createTokenClientPurposeEntry({ - tokenEntry: parsedTokenClientPurposeEntry.data, - kid, + const GSIPK_clientId = clientId; + const tokenGenStatesConsumerClients = + await readConsumerClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + if (tokenGenStatesConsumerClients.length === 0) { + return Promise.resolve(); + } else { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + + const seenKids = new Set(); + const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = + []; + + for (const entry of tokenGenStatesConsumerClients) { + const addedTokenGenStatesConsumerClient = await match( + clientEntry.clientPurposesIds.length + ) + .with(0, async () => { + const newTokenGenStatesConsumerClient = + createTokenGenStatesConsumerClient({ + tokenGenStatesClient: entry, + kid: extractKidFromTokenGenStatesEntryPK(entry.PK), clientId, - consumerId: parsedTokenClientPurposeEntry.data.consumerId, purposeId, purposeEntry, agreementEntry, catalogEntry, }); - await upsertTokenStateClientPurposeEntry( - newTokenClientPurposeEntry, + await upsertTokenGenStatesConsumerClient( + newTokenGenStatesConsumerClient, + dynamoDBClient + ); + await deleteClientEntryFromTokenGenerationStates( + entry, dynamoDBClient ); - seenKids.add(kid); - return newTokenClientPurposeEntry; - } + return newTokenGenStatesConsumerClient; + }) + .with(P.number.gt(0), async () => { + const kid = extractKidFromTokenGenStatesEntryPK(entry.PK); + if (!seenKids.has(kid)) { + const newTokenGenStatesConsumerClient = + createTokenGenStatesConsumerClient({ + tokenGenStatesClient: entry, + kid, + clientId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); + + await upsertTokenGenStatesConsumerClient( + newTokenGenStatesConsumerClient, + dynamoDBClient + ); + seenKids.add(kid); + return newTokenGenStatesConsumerClient; + } + return null; + }) + .run(); + + if (addedTokenGenStatesConsumerClient) { + // eslint-disable-next-line functional/immutable-data + addedTokenGenStatesConsumerClients.push( + addedTokenGenStatesConsumerClient + ); } + } - throw genericInternalError(`Unable to parse ${entry}`); - }) - ); - - // Second check for updated fields - await Promise.all( - addedTokenClientPurposeEntries.map(async (entry) => { - const { - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - } = await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient - ); + // Second check for updated fields + await Promise.all( + addedTokenGenStatesConsumerClients.map(async (entry) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); - await updateTokenDataForSecondRetrieval({ - dynamoDBClient, - entry, - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - }); - }) - ); + await updateTokenGenStatesDataForSecondRetrieval({ + dynamoDBClient, + entry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } } }) .with({ type: "ClientPurposeRemoved" }, async (msg) => { @@ -355,7 +390,7 @@ export async function handleMessageV1( const purposeIdToRemove = unsafeBrandId(msg.data.purposeId); const pk = makePlatformStatesClientPK(clientId); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (clientEntry) { if (clientEntry.version > msg.version) { @@ -381,7 +416,7 @@ export async function handleMessageV1( // token-generation-states if (updatedPurposeIds.length > 0) { - await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( GSIPK_clientId_purposeId, dynamoDBClient ); @@ -400,7 +435,7 @@ export async function handleMessageV1( await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); const GSIPK_clientId = clientId; - await deleteEntriesFromTokenStatesByClient( + await deleteEntriesFromTokenGenStatesByClientId( GSIPK_clientId, dynamoDBClient ); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 3cfcf28a5d..1f0462d9fa 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -2,9 +2,10 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { AuthorizationEventEnvelopeV2, Client, + clientKind, + clientKindTokenGenStates, ClientV2, fromClientV2, - genericInternalError, itemState, makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, @@ -16,30 +17,30 @@ import { missingKafkaMessageDataError, PlatformStatesClientEntry, PurposeId, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, unsafeBrandId, } from "pagopa-interop-models"; -import { match } from "ts-pattern"; +import { match, P } from "ts-pattern"; import { clientKindToTokenGenerationStatesClientKind, convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, - deleteEntriesFromTokenStatesByClient, - deleteEntriesFromTokenStatesByKid, - deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, - readClientEntry, - readClientEntriesInTokenGenerationStates, - deleteClientEntryFromTokenGenerationStatesTable, - extractKidFromTokenEntryPK, + deleteEntriesFromTokenGenStatesByClientId, + deleteEntriesFromTokenGenStatesByKid, + deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, + readPlatformClientEntry, + deleteClientEntryFromTokenGenerationStates, + extractKidFromTokenGenStatesEntryPK, extractAgreementIdFromAgreementPK, retrievePlatformStatesByPurpose, upsertPlatformClientEntry, - upsertTokenClientKidEntry, - upsertTokenStateClientPurposeEntry, + upsertTokenGenStatesApiClient, + upsertTokenGenStatesConsumerClient, setClientPurposeIdsInPlatformStatesEntry, - updateTokenDataForSecondRetrieval, - createTokenClientPurposeEntry, + updateTokenGenStatesDataForSecondRetrieval, + createTokenGenStatesConsumerClient, + readConsumerClientEntriesInTokenGenerationStates, } from "./utils.js"; export async function handleMessageV2( @@ -47,6 +48,7 @@ export async function handleMessageV2( dynamoDBClient: DynamoDBClient ): Promise { await match(message) + // eslint-disable-next-line sonarjs/cognitive-complexity .with({ type: "ClientKeyAdded" }, async (msg) => { const client = parseClient(msg.data.client, msg.type); @@ -58,7 +60,7 @@ export async function handleMessageV2( } const platformClientPK = makePlatformStatesClientPK(client.id); - const clientEntry = await readClientEntry( + const clientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -80,119 +82,146 @@ export async function handleMessageV2( } // token-generation-states - if (client.purposes.length > 0) { - const addedEntries = await Promise.all( - client.purposes.map(async (purposeId) => { - const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + await match(client.kind) + .with(clientKind.consumer, async () => { + if (client.purposes.length > 0) { + const addedEntries = await Promise.all( + client.purposes.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); + + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: msg.data.kid, + purposeId, + }); + + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenClientKidPurposePK, + consumerId: client.consumerId, + clientKind: clientKindTokenGenStates.consumer, + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(msg.data.kid), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...(purposeEntry + ? { + GSIPK_consumerId_eserviceId: + makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + } + : {}), + ...(purposeEntry && agreementEntry + ? { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: + agreementEntry.agreementDescriptorId, + }), + } + : {}), + ...(catalogEntry + ? { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + } + : {}), + }; + await upsertTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + return tokenGenStatesConsumerClient; + }) + ); - const tokenClientKidPurposePK = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: client.id, - kid: msg.data.kid, - purposeId, - }); + // Second check for updated fields + await Promise.all( + client.purposes.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient + ); - const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = + const addedTokenGenStatesConsumerClient = addedEntries[index]; + await updateTokenGenStatesDataForSecondRetrieval({ + dynamoDBClient, + entry: addedTokenGenStatesConsumerClient, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + }); + }) + ); + } else { + const tokenGenStatesConsumerClientWithoutPurpose: TokenGenerationStatesConsumerClient = { - PK: tokenClientKidPurposePK, + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: msg.data.kid, + }), consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind( - client.kind - ), + clientKind: clientKindTokenGenStates.consumer, publicKey: pem, - updatedAt: new Date().toISOString(), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(msg.data.kid), - GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ - clientId: client.id, - purposeId, - }), - GSIPK_purposeId: purposeId, - ...(purposeEntry - ? { - GSIPK_consumerId_eserviceId: - makeGSIPKConsumerIdEServiceId({ - consumerId: client.consumerId, - eserviceId: purposeEntry.purposeEserviceId, - }), - purposeState: purposeEntry.state, - purposeVersionId: purposeEntry.purposeVersionId, - } - : {}), - ...(purposeEntry && agreementEntry - ? { - agreementId: extractAgreementIdFromAgreementPK( - agreementEntry.PK - ), - agreementState: agreementEntry.state, - GSIPK_eserviceId_descriptorId: - makeGSIPKEServiceIdDescriptorId({ - eserviceId: purposeEntry.purposeEserviceId, - descriptorId: agreementEntry.agreementDescriptorId, - }), - } - : {}), - ...(catalogEntry - ? { - descriptorState: catalogEntry.state, - descriptorAudience: catalogEntry.descriptorAudience, - descriptorVoucherLifespan: - catalogEntry.descriptorVoucherLifespan, - } - : {}), + updatedAt: new Date().toISOString(), }; - - await upsertTokenStateClientPurposeEntry( - clientKidPurposeEntry, + await upsertTokenGenStatesConsumerClient( + tokenGenStatesConsumerClientWithoutPurpose, dynamoDBClient ); - return clientKidPurposeEntry; - }) - ); - - // Second check for updated fields - await Promise.all( - client.purposes.map(async (purposeId, index) => { - const { - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - } = await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient - ); - - const addedClientKidPurposeEntry = addedEntries[index]; - await updateTokenDataForSecondRetrieval({ - dynamoDBClient, - entry: addedClientKidPurposeEntry, - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - }); - }) - ); - } else { - const clientKidEntry: TokenGenerationStatesClientEntry = { - PK: makeTokenGenerationStatesClientKidPK({ - clientId: client.id, - kid: msg.data.kid, - }), - consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), - publicKey: pem, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(msg.data.kid), - updatedAt: new Date().toISOString(), - }; - await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); - } + } + }) + .with(clientKind.api, async () => { + const tokenGenStatesApiClient: TokenGenerationStatesApiClient = { + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: msg.data.kid, + }), + consumerId: client.consumerId, + clientKind: clientKindTokenGenStates.api, + publicKey: pem, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(msg.data.kid), + updatedAt: new Date().toISOString(), + }; + await upsertTokenGenStatesApiClient( + tokenGenStatesApiClient, + dynamoDBClient + ); + }) + .exhaustive(); }) .with({ type: "ClientKeyDeleted" }, async (msg) => { const client = parseClient(msg.data.client, msg.type); const pk = makePlatformStatesClientPK(client.id); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (clientEntry && clientEntry.version > msg.version) { return Promise.resolve(); @@ -210,14 +239,14 @@ export async function handleMessageV2( } const GSIPK_kid = makeGSIPKKid(msg.data.kid); - await deleteEntriesFromTokenStatesByKid(GSIPK_kid, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByKid(GSIPK_kid, dynamoDBClient); }) .with({ type: "ClientPurposeAdded" }, async (msg) => { const client = parseClient(msg.data.client, msg.type); // platform-states const platformClientEntryPK = makePlatformStatesClientPK(client.id); - const clientEntry = await readClientEntry( + const clientEntry = await readPlatformClientEntry( platformClientEntryPK, dynamoDBClient ); @@ -239,11 +268,12 @@ export async function handleMessageV2( // token-generation-states const GSIPK_clientId = client.id; - const tokenClientEntries = await readClientEntriesInTokenGenerationStates( - GSIPK_clientId, - dynamoDBClient - ); - if (tokenClientEntries.length === 0) { + const tokenGenStatesConsumerClients = + await readConsumerClientEntriesInTokenGenerationStates( + GSIPK_clientId, + dynamoDBClient + ); + if (tokenGenStatesConsumerClients.length === 0) { return Promise.resolve(); } else { const purposeId = unsafeBrandId(msg.data.purposeId); @@ -251,69 +281,71 @@ export async function handleMessageV2( await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); const seenKids = new Set(); - const addedTokenClientPurposeEntries = await Promise.all( - tokenClientEntries.map(async (entry) => { - const parsedTokenClientEntry = - TokenGenerationStatesClientEntry.safeParse(entry); - const parsedTokenClientPurposeEntry = - TokenGenerationStatesClientPurposeEntry.safeParse(entry); + const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = + []; - if (parsedTokenClientEntry.success) { - const newTokenClientPurposeEntry = createTokenClientPurposeEntry({ - tokenEntry: parsedTokenClientEntry.data, - kid: extractKidFromTokenEntryPK(parsedTokenClientEntry.data.PK), - clientId: client.id, - consumerId: client.consumerId, - purposeId, - purposeEntry, - agreementEntry, - catalogEntry, - }); + for (const entry of tokenGenStatesConsumerClients) { + const addedTokenGenStatesConsumerClient = await match( + client.purposes.length + ) + .with(1, async () => { + const newTokenGenStatesConsumerClient = + createTokenGenStatesConsumerClient({ + tokenGenStatesClient: entry, + kid: extractKidFromTokenGenStatesEntryPK(entry.PK), + clientId: client.id, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); - await upsertTokenStateClientPurposeEntry( - newTokenClientPurposeEntry, + await upsertTokenGenStatesConsumerClient( + newTokenGenStatesConsumerClient, dynamoDBClient ); - await deleteClientEntryFromTokenGenerationStatesTable( + await deleteClientEntryFromTokenGenerationStates( entry, dynamoDBClient ); - return newTokenClientPurposeEntry; - } - - if (parsedTokenClientPurposeEntry.success) { - const kid = extractKidFromTokenEntryPK( - parsedTokenClientPurposeEntry.data.PK - ); + return newTokenGenStatesConsumerClient; + }) + .with(P.number.gt(1), async () => { + const kid = extractKidFromTokenGenStatesEntryPK(entry.PK); if (!seenKids.has(kid)) { - const newTokenClientPurposeEntry = - createTokenClientPurposeEntry({ - tokenEntry: parsedTokenClientPurposeEntry.data, + const newTokenGenStatesConsumerClient = + createTokenGenStatesConsumerClient({ + tokenGenStatesClient: entry, kid, clientId: client.id, - consumerId: client.consumerId, purposeId, purposeEntry, agreementEntry, catalogEntry, }); - await upsertTokenStateClientPurposeEntry( - newTokenClientPurposeEntry, + await upsertTokenGenStatesConsumerClient( + newTokenGenStatesConsumerClient, dynamoDBClient ); seenKids.add(kid); - return newTokenClientPurposeEntry; + return newTokenGenStatesConsumerClient; } - } + return null; + }) + .run(); - throw genericInternalError(`Unable to parse ${entry}`); - }) - ); + if (addedTokenGenStatesConsumerClient) { + // eslint-disable-next-line functional/immutable-data + addedTokenGenStatesConsumerClients.push( + addedTokenGenStatesConsumerClient + ); + } + } // Second check for updated fields await Promise.all( - addedTokenClientPurposeEntries.map(async (entry) => { + addedTokenGenStatesConsumerClients.map(async (entry) => { const { purposeEntry: purposeEntry2, agreementEntry: agreementEntry2, @@ -323,7 +355,7 @@ export async function handleMessageV2( dynamoDBClient ); - await updateTokenDataForSecondRetrieval({ + await updateTokenGenStatesDataForSecondRetrieval({ dynamoDBClient, entry, purposeEntry: purposeEntry2, @@ -337,7 +369,7 @@ export async function handleMessageV2( .with({ type: "ClientPurposeRemoved" }, async (msg) => { const client = parseClient(msg.data.client, msg.type); const pk = makePlatformStatesClientPK(client.id); - const clientEntry = await readClientEntry(pk, dynamoDBClient); + const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (clientEntry) { if (clientEntry.version > msg.version) { @@ -356,7 +388,7 @@ export async function handleMessageV2( // token-generation-states if (client.purposes.length > 0) { - await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( GSIPK_clientId_purposeId, dynamoDBClient ); @@ -375,7 +407,7 @@ export async function handleMessageV2( await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); const GSIPK_clientId = client.id; - await deleteEntriesFromTokenStatesByClient( + await deleteEntriesFromTokenGenStatesByClientId( GSIPK_clientId, dynamoDBClient ); diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 2e2055156b..74bc4788c2 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -17,8 +17,8 @@ import { ClientId, clientKind, ClientKind, - clientKindTokenStates, - ClientKindTokenStates, + clientKindTokenGenStates, + ClientKindTokenGenStates, genericInternalError, GSIPKClientIdPurposeId, GSIPKConsumerIdEServiceId, @@ -26,7 +26,6 @@ import { makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, - makeGSIPKKid, makePlatformStatesEServiceDescriptorPK, makePlatformStatesPurposePK, makeTokenGenerationStatesClientKidPK, @@ -40,12 +39,11 @@ import { PlatformStatesPurposeEntry, PlatformStatesPurposePK, PurposeId, - TenantId, - TokenGenerationStatesClientEntry, + TokenGenerationStatesApiClient, TokenGenerationStatesClientKidPK, TokenGenerationStatesClientKidPurposePK, - TokenGenerationStatesClientPurposeEntry, - TokenGenerationStatesGenericEntry, + TokenGenerationStatesConsumerClient, + TokenGenerationStatesGenericClient, } from "pagopa-interop-models"; import { z } from "zod"; import { unmarshall } from "@aws-sdk/util-dynamodb"; @@ -54,7 +52,7 @@ import { UpdateItemInput } from "@aws-sdk/client-dynamodb"; import { UpdateItemCommand } from "@aws-sdk/client-dynamodb"; import { config } from "./config/config.js"; -export const deleteEntriesFromTokenStatesByKid = async ( +export const deleteEntriesFromTokenGenStatesByKid = async ( GSIPK_kid: GSIPKKid, dynamoDBClient: DynamoDBClient ): Promise => { @@ -81,23 +79,20 @@ export const deleteEntriesFromTokenStatesByKid = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesGenericEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesGenericClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } - for (const entry of tokenStateEntries.data) { - await deleteClientEntryFromTokenGenerationStatesTable( - entry, - dynamoDBClient - ); + for (const entry of tokenGenStatesEntries.data) { + await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); } if (data.LastEvaluatedKey) { @@ -127,7 +122,7 @@ export const deleteClientEntryFromPlatformStates = async ( await dynamoDBClient.send(command); }; -export const deleteEntriesFromTokenStatesByClient = async ( +export const deleteEntriesFromTokenGenStatesByClientId = async ( GSIPK_client: ClientId, dynamoDBClient: DynamoDBClient ): Promise => { @@ -155,23 +150,20 @@ export const deleteEntriesFromTokenStatesByClient = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesGenericEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesGenericClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } - for (const entry of tokenStateEntries.data) { - await deleteClientEntryFromTokenGenerationStatesTable( - entry, - dynamoDBClient - ); + for (const entry of tokenGenStatesEntries.data) { + await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); } if (data.LastEvaluatedKey) { @@ -187,8 +179,8 @@ export const deleteEntriesFromTokenStatesByClient = async ( await runPaginatedQuery(GSIPK_client, dynamoDBClient, undefined); }; -export const deleteClientEntryFromTokenGenerationStatesTable = async ( - entryToDelete: TokenGenerationStatesGenericEntry, +export const deleteClientEntryFromTokenGenerationStates = async ( + entryToDelete: TokenGenerationStatesGenericClient, dynamoDBClient: DynamoDBClient ): Promise => { const input: DeleteItemInput = { @@ -201,7 +193,7 @@ export const deleteClientEntryFromTokenGenerationStatesTable = async ( await dynamoDBClient.send(command); }; -export const readClientEntry = async ( +export const readPlatformClientEntry = async ( primaryKey: PlatformStatesClientPK, dynamoDBClient: DynamoDBClient ): Promise => { @@ -231,12 +223,12 @@ export const readClientEntry = async ( } }; -const readTokenStateEntriesByGSIPKClientPurpose = async ( +const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise<{ - tokenStateEntries: TokenGenerationStatesClientPurposeEntry[]; + tokenGenStatesEntries: TokenGenerationStatesConsumerClient[]; lastEvaluatedKey: Record | undefined; }> => { const input: QueryInput = { @@ -258,57 +250,53 @@ const readTokenStateEntriesByGSIPKClientPurpose = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } return { - tokenStateEntries: tokenStateEntries.data, + tokenGenStatesEntries: tokenGenStatesEntries.data, lastEvaluatedKey: data.LastEvaluatedKey, }; } }; -export const deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable = - async ( +export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( + GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, + dynamoDBClient: DynamoDBClient +): Promise => { + const runPaginatedQuery = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + exclusiveStartKey?: Record ): Promise => { - const runPaginatedQuery = async ( - GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, - dynamoDBClient: DynamoDBClient, - exclusiveStartKey?: Record - ): Promise => { - const res = await readTokenStateEntriesByGSIPKClientPurpose( + const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurpose( + GSIPK_clientId_purposeId, + dynamoDBClient, + exclusiveStartKey + ); + + for (const entry of res.tokenGenStatesEntries) { + await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); + } + + if (res.lastEvaluatedKey) { + await runPaginatedQuery( GSIPK_clientId_purposeId, dynamoDBClient, - exclusiveStartKey + res.lastEvaluatedKey ); - - for (const entry of res.tokenStateEntries) { - await deleteClientEntryFromTokenGenerationStatesTable( - entry, - dynamoDBClient - ); - } - - if (res.lastEvaluatedKey) { - await runPaginatedQuery( - GSIPK_clientId_purposeId, - dynamoDBClient, - res.lastEvaluatedKey - ); - } - }; - await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); + } }; + await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); +}; export const convertEntriesToClientKidInTokenGenerationStates = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, @@ -318,16 +306,16 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { - const res = await readTokenStateEntriesByGSIPKClientPurpose( + ): Promise => { + const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurpose( GSIPK_clientId_purposeId, dynamoDBClient, exclusiveStartKey ); // convert entries - for (const entry of res.tokenStateEntries) { - const newEntry: TokenGenerationStatesClientEntry = { + for (const entry of res.tokenGenStatesEntries) { + const newEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: entry.GSIPK_clientId, kid: entry.GSIPK_kid, @@ -341,20 +329,17 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( }; // write the new one - await writeTokenStateClientEntry(newEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient(newEntry, dynamoDBClient); // delete the old one - await deleteClientEntryFromTokenGenerationStatesTable( - entry, - dynamoDBClient - ); + await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); } if (!res.lastEvaluatedKey) { - return res.tokenStateEntries; + return res.tokenGenStatesEntries; } else { return [ - ...res.tokenStateEntries, + ...res.tokenGenStatesEntries, ...(await runPaginatedQuery( GSIPK_clientId_purposeId, dynamoDBClient, @@ -366,33 +351,33 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( await runPaginatedQuery(GSIPK_clientId_purposeId, dynamoDBClient); }; -export const writeTokenStateClientEntry = async ( - tokenStateEntry: TokenGenerationStatesClientEntry, +export const writeTokenGenStatesApiClient = async ( + tokenGenStatesApiClient: TokenGenerationStatesApiClient, dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", Item: { PK: { - S: tokenStateEntry.PK, + S: tokenGenStatesApiClient.PK, }, updatedAt: { - S: tokenStateEntry.updatedAt, + S: tokenGenStatesApiClient.updatedAt, }, consumerId: { - S: tokenStateEntry.consumerId, + S: tokenGenStatesApiClient.consumerId, }, clientKind: { - S: tokenStateEntry.clientKind, + S: tokenGenStatesApiClient.clientKind, }, publicKey: { - S: tokenStateEntry.publicKey, + S: tokenGenStatesApiClient.publicKey, }, GSIPK_clientId: { - S: tokenStateEntry.GSIPK_clientId, + S: tokenGenStatesApiClient.GSIPK_clientId, }, GSIPK_kid: { - S: tokenStateEntry.GSIPK_kid, + S: tokenGenStatesApiClient.GSIPK_kid, }, }, TableName: config.tokenGenerationReadModelTableNameTokenGeneration, @@ -401,7 +386,7 @@ export const writeTokenStateClientEntry = async ( await dynamoDBClient.send(command); }; -export const readCatalogEntry = async ( +export const readPlatformCatalogEntry = async ( primaryKey: PlatformStatesEServiceDescriptorPK, dynamoDBClient: DynamoDBClient ): Promise => { @@ -497,109 +482,227 @@ export const readPlatformPurposeEntry = async ( } }; -export const upsertTokenStateClientPurposeEntry = async ( - tokenStateEntry: TokenGenerationStatesClientPurposeEntry, +export const upsertTokenGenStatesConsumerClient = async ( + tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient, dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { Item: { PK: { - S: tokenStateEntry.PK, + S: tokenGenStatesConsumerClient.PK, }, - ...(tokenStateEntry.descriptorState + ...(tokenGenStatesConsumerClient.descriptorState ? { descriptorState: { - S: tokenStateEntry.descriptorState, + S: tokenGenStatesConsumerClient.descriptorState, }, } : {}), - ...(tokenStateEntry.descriptorAudience + ...(tokenGenStatesConsumerClient.descriptorAudience ? { descriptorAudience: { - L: tokenStateEntry.descriptorAudience.map((item) => ({ - S: item, - })), + L: tokenGenStatesConsumerClient.descriptorAudience.map( + (item) => ({ + S: item, + }) + ), }, } : {}), - ...(tokenStateEntry.descriptorVoucherLifespan + ...(tokenGenStatesConsumerClient.descriptorVoucherLifespan ? { descriptorVoucherLifespan: { - N: tokenStateEntry.descriptorVoucherLifespan.toString(), + N: tokenGenStatesConsumerClient.descriptorVoucherLifespan.toString(), }, } : {}), updatedAt: { - S: tokenStateEntry.updatedAt, + S: tokenGenStatesConsumerClient.updatedAt, }, consumerId: { - S: tokenStateEntry.consumerId, + S: tokenGenStatesConsumerClient.consumerId, }, - ...(tokenStateEntry.agreementId + ...(tokenGenStatesConsumerClient.agreementId ? { agreementId: { - S: tokenStateEntry.agreementId, + S: tokenGenStatesConsumerClient.agreementId, }, } : {}), - ...(tokenStateEntry.purposeVersionId + ...(tokenGenStatesConsumerClient.purposeVersionId ? { purposeVersionId: { - S: tokenStateEntry.purposeVersionId, + S: tokenGenStatesConsumerClient.purposeVersionId, }, } : {}), - ...(tokenStateEntry.GSIPK_consumerId_eserviceId + ...(tokenGenStatesConsumerClient.GSIPK_consumerId_eserviceId ? { GSIPK_consumerId_eserviceId: { - S: tokenStateEntry.GSIPK_consumerId_eserviceId, + S: tokenGenStatesConsumerClient.GSIPK_consumerId_eserviceId, }, } : {}), clientKind: { - S: tokenStateEntry.clientKind, + S: tokenGenStatesConsumerClient.clientKind, }, publicKey: { - S: tokenStateEntry.publicKey, + S: tokenGenStatesConsumerClient.publicKey, }, GSIPK_clientId: { - S: tokenStateEntry.GSIPK_clientId, + S: tokenGenStatesConsumerClient.GSIPK_clientId, }, GSIPK_kid: { - S: tokenStateEntry.GSIPK_kid, + S: tokenGenStatesConsumerClient.GSIPK_kid, }, - ...(tokenStateEntry.GSIPK_clientId_purposeId + ...(tokenGenStatesConsumerClient.GSIPK_clientId_purposeId ? { GSIPK_clientId_purposeId: { - S: tokenStateEntry.GSIPK_clientId_purposeId, + S: tokenGenStatesConsumerClient.GSIPK_clientId_purposeId, }, } : {}), - ...(tokenStateEntry.agreementState + ...(tokenGenStatesConsumerClient.agreementState ? { agreementState: { - S: tokenStateEntry.agreementState, + S: tokenGenStatesConsumerClient.agreementState, }, } : {}), - ...(tokenStateEntry.GSIPK_eserviceId_descriptorId + ...(tokenGenStatesConsumerClient.GSIPK_eserviceId_descriptorId ? { GSIPK_eserviceId_descriptorId: { - S: tokenStateEntry.GSIPK_eserviceId_descriptorId, + S: tokenGenStatesConsumerClient.GSIPK_eserviceId_descriptorId, }, } : {}), - ...(tokenStateEntry.GSIPK_purposeId + ...(tokenGenStatesConsumerClient.GSIPK_purposeId ? { GSIPK_purposeId: { - S: tokenStateEntry.GSIPK_purposeId, + S: tokenGenStatesConsumerClient.GSIPK_purposeId, }, } : {}), - ...(tokenStateEntry.purposeState + ...(tokenGenStatesConsumerClient.purposeState ? { purposeState: { - S: tokenStateEntry.purposeState, + S: tokenGenStatesConsumerClient.purposeState, + }, + } + : {}), + }, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; + +export const writeTokenGenStatesConsumerClient = async ( + tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: tokenGenStatesConsumerClient.PK, + }, + ...(tokenGenStatesConsumerClient.descriptorState + ? { + descriptorState: { + S: tokenGenStatesConsumerClient.descriptorState, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.descriptorAudience + ? { + descriptorAudience: { + L: tokenGenStatesConsumerClient.descriptorAudience.map( + (item) => ({ + S: item, + }) + ), + }, + } + : {}), + ...(tokenGenStatesConsumerClient.descriptorVoucherLifespan + ? { + descriptorVoucherLifespan: { + N: tokenGenStatesConsumerClient.descriptorVoucherLifespan.toString(), + }, + } + : {}), + updatedAt: { + S: tokenGenStatesConsumerClient.updatedAt, + }, + consumerId: { + S: tokenGenStatesConsumerClient.consumerId, + }, + ...(tokenGenStatesConsumerClient.agreementId + ? { + agreementId: { + S: tokenGenStatesConsumerClient.agreementId, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.purposeVersionId + ? { + purposeVersionId: { + S: tokenGenStatesConsumerClient.purposeVersionId, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.GSIPK_consumerId_eserviceId + ? { + GSIPK_consumerId_eserviceId: { + S: tokenGenStatesConsumerClient.GSIPK_consumerId_eserviceId, + }, + } + : {}), + clientKind: { + S: tokenGenStatesConsumerClient.clientKind, + }, + publicKey: { + S: tokenGenStatesConsumerClient.publicKey, + }, + GSIPK_clientId: { + S: tokenGenStatesConsumerClient.GSIPK_clientId, + }, + GSIPK_kid: { + S: tokenGenStatesConsumerClient.GSIPK_kid, + }, + ...(tokenGenStatesConsumerClient.GSIPK_clientId_purposeId + ? { + GSIPK_clientId_purposeId: { + S: tokenGenStatesConsumerClient.GSIPK_clientId_purposeId, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.agreementState + ? { + agreementState: { + S: tokenGenStatesConsumerClient.agreementState, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.GSIPK_eserviceId_descriptorId + ? { + GSIPK_eserviceId_descriptorId: { + S: tokenGenStatesConsumerClient.GSIPK_eserviceId_descriptorId, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.GSIPK_purposeId + ? { + GSIPK_purposeId: { + S: tokenGenStatesConsumerClient.GSIPK_purposeId, + }, + } + : {}), + ...(tokenGenStatesConsumerClient.purposeState + ? { + purposeState: { + S: tokenGenStatesConsumerClient.purposeState, }, } : {}), @@ -612,13 +715,13 @@ export const upsertTokenStateClientPurposeEntry = async ( export const clientKindToTokenGenerationStatesClientKind = ( kind: ClientKind -): ClientKindTokenStates => - match(kind) - .with(clientKind.consumer, () => clientKindTokenStates.consumer) - .with(clientKind.api, () => clientKindTokenStates.api) +): ClientKindTokenGenStates => + match(kind) + .with(clientKind.consumer, () => clientKindTokenGenStates.consumer) + .with(clientKind.api, () => clientKindTokenGenStates.api) .exhaustive(); -export const writeClientEntry = async ( +export const writePlatformClientEntry = async ( clientEntry: PlatformStatesClientEntry, dynamoDBClient: DynamoDBClient ): Promise => { @@ -655,15 +758,15 @@ export const writeClientEntry = async ( await dynamoDBClient.send(command); }; -export const readClientEntriesInTokenGenerationStates = async ( +export const readConsumerClientEntriesInTokenGenerationStates = async ( GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Client", @@ -687,7 +790,7 @@ export const readClientEntriesInTokenGenerationStates = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const clientEntries = z - .array(TokenGenerationStatesGenericEntry) + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); if (!clientEntries.success) { @@ -757,7 +860,7 @@ export const setClientPurposeIdsInPlatformStatesEntry = async ( await dynamoDBClient.send(command); }; -export const extractKidFromTokenEntryPK = ( +export const extractKidFromTokenGenStatesEntryPK = ( pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK ): string => pk.split("#")[2]; @@ -820,7 +923,10 @@ export const retrievePlatformStatesByPurpose = async ( eserviceId: purposeEntry.purposeEserviceId, descriptorId: agreementEntry.agreementDescriptorId, }); - const catalogEntry = await readCatalogEntry(catalogPK, dynamoDBClient); + const catalogEntry = await readPlatformCatalogEntry( + catalogPK, + dynamoDBClient + ); if (!catalogEntry) { return { @@ -872,8 +978,8 @@ export const upsertPlatformClientEntry = async ( await dynamoDBClient.send(command); }; -export const upsertTokenClientKidEntry = async ( - entry: TokenGenerationStatesClientEntry, +export const upsertTokenGenStatesApiClient = async ( + entry: TokenGenerationStatesApiClient, dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { @@ -906,7 +1012,7 @@ export const upsertTokenClientKidEntry = async ( await dynamoDBClient.send(command); }; -export const updateTokenDataForSecondRetrieval = async ({ +export const updateTokenGenStatesDataForSecondRetrieval = async ({ dynamoDBClient, entry, purposeEntry, @@ -914,17 +1020,15 @@ export const updateTokenDataForSecondRetrieval = async ({ catalogEntry, }: { dynamoDBClient: DynamoDBClient; - entry: TokenGenerationStatesClientPurposeEntry; + entry: TokenGenerationStatesConsumerClient; purposeEntry?: PlatformStatesPurposeEntry; agreementEntry?: PlatformStatesAgreementEntry; catalogEntry?: PlatformStatesCatalogEntry; }): Promise => { - const setIfChanged = < - K extends keyof TokenGenerationStatesClientPurposeEntry - >( + const setIfChanged = ( key: K, - newValue: TokenGenerationStatesClientPurposeEntry[K] - ): Partial => { + newValue: TokenGenerationStatesConsumerClient[K] + ): Partial => { const oldValue = entry[key]; if (Array.isArray(oldValue) && Array.isArray(newValue)) { @@ -935,7 +1039,7 @@ export const updateTokenDataForSecondRetrieval = async ({ return oldValue !== newValue ? { [key]: newValue } : {}; }; - const updatedFields: Partial = { + const updatedFields: Partial = { ...(purposeEntry ? { ...setIfChanged( @@ -1016,7 +1120,7 @@ const convertValueToAttributeValue = ( }; const convertToExpressionAttributeValues = ( - updatedFields: Partial + updatedFields: Partial ): Record => { const expressionAttributeValues = Object.keys(updatedFields).reduce( (acc, key) => { @@ -1040,7 +1144,7 @@ const convertToExpressionAttributeValues = ( }; const generateUpdateItemInputData = ( - updatedFields: Partial + updatedFields: Partial ): { updateExpression: string; expressionAttributeValues: Record; @@ -1060,45 +1164,37 @@ const generateUpdateItemInputData = ( }; }; -export const createTokenClientPurposeEntry = ({ - tokenEntry: baseEntry, +export const createTokenGenStatesConsumerClient = ({ + tokenGenStatesClient, kid, clientId, - consumerId, purposeId, purposeEntry, agreementEntry, catalogEntry, }: { - tokenEntry: TokenGenerationStatesGenericEntry; + tokenGenStatesClient: TokenGenerationStatesConsumerClient; kid: string; clientId: ClientId; - consumerId: TenantId; purposeId: PurposeId; purposeEntry?: PlatformStatesPurposeEntry; agreementEntry?: PlatformStatesAgreementEntry; catalogEntry?: PlatformStatesCatalogEntry; -}): TokenGenerationStatesClientPurposeEntry => { +}): TokenGenerationStatesConsumerClient => { const pk = makeTokenGenerationStatesClientKidPurposePK({ clientId, kid, purposeId, }); - const isTokenClientPurposeEntry = - TokenGenerationStatesClientPurposeEntry.safeParse(baseEntry).success; return { PK: pk, - consumerId: baseEntry.consumerId, + consumerId: tokenGenStatesClient.consumerId, updatedAt: new Date().toISOString(), - clientKind: isTokenClientPurposeEntry - ? baseEntry.clientKind - : clientKindTokenStates.consumer, - publicKey: baseEntry.publicKey, - GSIPK_clientId: baseEntry.GSIPK_clientId, - GSIPK_kid: isTokenClientPurposeEntry - ? baseEntry.GSIPK_kid - : makeGSIPKKid(kid), + clientKind: clientKindTokenGenStates.consumer, + publicKey: tokenGenStatesClient.publicKey, + GSIPK_clientId: tokenGenStatesClient.GSIPK_clientId, + GSIPK_kid: tokenGenStatesClient.GSIPK_kid, GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, purposeId, @@ -1106,7 +1202,7 @@ export const createTokenClientPurposeEntry = ({ GSIPK_purposeId: purposeId, ...(purposeEntry && { GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId, + consumerId: tokenGenStatesClient.consumerId, eserviceId: purposeEntry.purposeEserviceId, }), purposeState: purposeEntry.state, diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index 6da35e8c62..ea3bf680e6 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -14,27 +14,29 @@ import { buildDynamoDBTables, deleteDynamoDBTables, getMockClient, - getMockTokenStatesClientEntry, - getMockTokenStatesClientPurposeEntry, - readAllPlatformStateItems, - readAllTokenStateItems, + getMockTokenGenStatesApiClient, + getMockTokenGenStatesConsumerClient, + readAllPlatformStatesItems, + readAllTokenGenStatesItems, getMockPurpose, getMockPurposeVersion, writePlatformPurposeEntry, getMockAgreement, writePlatformAgreementEntry, getMockDescriptor, - writeCatalogEntry, + writePlatformCatalogEntry, getMockKey, - writeTokenStateEntry, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { + Agreement, AuthorizationEventEnvelope, Client, ClientAddedV1, ClientComponentStateV1, ClientDeletedV1, ClientId, + clientKindTokenGenStates, ClientPurposeAddedV1, ClientPurposeRemovedV1, Descriptor, @@ -61,16 +63,16 @@ import { purposeVersionState, TenantId, toClientV1, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, toKeyV1, } from "pagopa-interop-models"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { clientKindToTokenGenerationStatesClientKind, - readClientEntry, - writeClientEntry, - writeTokenStateClientEntry, + readPlatformClientEntry, + writePlatformClientEntry, + writeTokenGenStatesApiClient, } from "../src/utils.js"; import { config } from "./utils.js"; @@ -127,12 +129,15 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -160,13 +165,13 @@ describe("integration tests V1 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -212,12 +217,15 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -275,34 +283,37 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: key.kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should do no operation if the existing table entry doesn't exist", async () => { @@ -336,7 +347,7 @@ describe("integration tests V1 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -344,27 +355,27 @@ describe("integration tests V1 events", async () => { clientId: client.id, kid: key.kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toBeUndefined(); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should update platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { @@ -483,7 +494,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -500,7 +511,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -512,7 +523,10 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPurposePK1 = @@ -535,31 +549,35 @@ describe("integration tests V1 events", async () => { clientId: client.id, purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_purposeId: purpose2.id, - }; - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -571,12 +589,12 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, + ...tokenConsumerClient1, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -605,9 +623,9 @@ describe("integration tests V1 events", async () => { }), updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, + ...tokenConsumerClient2, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -637,13 +655,13 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(4); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(4); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -765,7 +783,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -782,7 +800,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -794,7 +812,10 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPurposePK1 = @@ -839,51 +860,59 @@ describe("integration tests V1 events", async () => { purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_purposeId: purpose2.id, - }; - const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK3), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK4), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, - GSIPK_purposeId: purpose2.id, - }; - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK3), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK4), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient3, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient4, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -895,12 +924,12 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, + ...tokenConsumerClient1, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -929,9 +958,9 @@ describe("integration tests V1 events", async () => { }), updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, + ...tokenConsumerClient2, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -961,13 +990,13 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(4); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(4); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -1013,25 +1042,28 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: oldKey.kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1043,24 +1075,24 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) ); }); @@ -1106,7 +1138,10 @@ describe("integration tests V1 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ @@ -1117,24 +1152,24 @@ describe("integration tests V1 events", async () => { clientId: client.id, kid: addedKey.kid, }); - const tokenClientEntry1: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK1), + const tokenClientEntry1: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK1), consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - const tokenClientEntry2: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK2), + const tokenClientEntry2: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK2), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), }; - await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); - await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry2, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1146,24 +1181,24 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([tokenClientEntry1, expectedTokenClientEntry]) ); }); @@ -1207,34 +1242,37 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: kidToRemove, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kidToRemove), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should insert platform-states entry and delete token-generation-states entries for that kid", async () => { @@ -1265,7 +1303,7 @@ describe("integration tests V1 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -1276,44 +1314,43 @@ describe("integration tests V1 events", async () => { purposeId, }); - const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), - GSIPK_clientId: client.id, - }; - const tokenClientPurposeEntryWithOtherKid: TokenGenerationStatesClientPurposeEntry = + const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + const tokenConsumerClientWithOtherKid: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_clientId: client.id, }; - await writeTokenStateEntry( - tokenClientPurposeEntryWithKid, + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithKid, dynamoDBClient ); - await writeTokenStateEntry( - tokenClientPurposeEntryWithOtherKid, + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithOtherKid, dynamoDBClient ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformStatesEntry).toBeUndefined(); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntryWithOtherKid, - tokenClientPurposeEntryWithKid, + tokenConsumerClientWithOtherKid, + tokenConsumerClientWithKid, ]) ); }); @@ -1356,7 +1393,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purposeId], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPurposePK = @@ -1370,24 +1410,23 @@ describe("integration tests V1 events", async () => { kid: otherKid, }); - const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), - GSIPK_clientId: client.id, - }; + const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; - const tokenClientEntryWithOtherKid: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntryWithOtherKid: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(otherKid), }; - await writeTokenStateEntry( - tokenClientPurposeEntryWithKid, + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithKid, dynamoDBClient ); - await writeTokenStateClientEntry( + await writeTokenGenStatesApiClient( tokenClientEntryWithOtherKid, dynamoDBClient ); @@ -1395,7 +1434,7 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1407,10 +1446,12 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntryWithOtherKid]); + expect(retrievedTokenGenStatesEntries).toEqual([ + tokenClientEntryWithOtherKid, + ]); }); }); @@ -1457,32 +1498,35 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should do no operation if the entry doesn't exist in platform-states", async () => { @@ -1518,7 +1562,7 @@ describe("integration tests V1 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -1526,26 +1570,26 @@ describe("integration tests V1 events", async () => { clientId: client.id, kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toBeUndefined(); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should update platform-states entry", async () => { @@ -1591,20 +1635,26 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = - getMockTokenStatesClientPurposeEntry(); - const tokenClientEntry: TokenGenerationStatesClientEntry = - getMockTokenStatesClientEntry(); - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + const tokenConsumerClient: TokenGenerationStatesConsumerClient = + getMockTokenGenStatesConsumerClient(); + const tokenClientEntry: TokenGenerationStatesApiClient = + getMockTokenGenStatesApiClient(); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient, + dynamoDBClient + ); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1617,13 +1667,13 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( - expect.arrayContaining([tokenClientPurposeEntry, tokenClientEntry]) + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([tokenConsumerClient, tokenClientEntry]) ); }); @@ -1676,14 +1726,18 @@ describe("integration tests V1 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry, dynamoDBClient); - const agreement = getMockAgreement(); + const agreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose.eserviceId, + }; const platformAgreementEntry: PlatformStatesAgreementEntry = { PK: makePlatformStatesAgreementPK(agreement.id), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose.consumerId, + consumerId, eserviceId: purpose.eserviceId, }), GSISK_agreementTimestamp: new Date().toISOString(), @@ -1706,7 +1760,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -1718,7 +1772,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const kid1 = "KID1"; @@ -1732,30 +1789,38 @@ describe("integration tests V1 events", async () => { kid: kid2, }); - const tokenClientEntry1: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK1), + const tokenClientEntry1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK1), + consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kid1), }; - const tokenClientEntry2: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK2), + const tokenClientEntry2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), + consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kid2), }; - const tokenClientPurposeEntryWithOtherClient = - getMockTokenStatesClientPurposeEntry(); + const tokenConsumerClientWithOtherClient = + getMockTokenGenStatesConsumerClient(); - await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); - await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); - await writeTokenStateEntry( - tokenClientPurposeEntryWithOtherClient, + await writeTokenGenStatesConsumerClient( + tokenClientEntry1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenClientEntry2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithOtherClient, dynamoDBClient ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1768,10 +1833,11 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const newTokenClientPurposeEntryData = { + const newTokenConsumerClientData = { + clientKind: clientKindTokenGenStates.consumer, GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose.id, @@ -1794,10 +1860,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { ...tokenClientEntry1, - ...newTokenClientPurposeEntryData, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose.id, @@ -1805,10 +1871,10 @@ describe("integration tests V1 events", async () => { }), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { ...tokenClientEntry2, - ...newTokenClientPurposeEntryData, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose.id, @@ -1816,12 +1882,12 @@ describe("integration tests V1 events", async () => { }), }; - expect(retrievedTokenEntries).toHaveLength(3); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(3); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntryWithOtherClient, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClientWithOtherClient, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -1943,7 +2009,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -1960,7 +2026,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -1972,7 +2038,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purpose1.id], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const kid1 = "KID1"; @@ -1994,29 +2063,33 @@ describe("integration tests V1 events", async () => { clientId: client.id, purposeId: purpose1.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); - await writeTokenStateClientEntry( + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, dynamoDBClient ); @@ -2024,7 +2097,7 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -2037,10 +2110,10 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const newTokenClientPurposeEntryData = { + const newTokenConsumerClientData = { GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -2063,10 +2136,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient1, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, @@ -2074,10 +2147,10 @@ describe("integration tests V1 events", async () => { }), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient2, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, @@ -2085,14 +2158,14 @@ describe("integration tests V1 events", async () => { }), }; - expect(retrievedTokenEntries).toHaveLength(5); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(5); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ tokenClientEntryWithOtherClient, - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -2214,7 +2287,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -2231,7 +2304,7 @@ describe("integration tests V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -2243,7 +2316,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purpose1.id], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const kid1 = "KID1"; @@ -2277,49 +2353,57 @@ describe("integration tests V1 events", async () => { clientId: client.id, purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK1), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK2), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK3), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), - GSIPK_purposeId: purpose2.id, - }; - const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK4), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), - GSIPK_purposeId: purpose2.id, - }; - const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK3), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose2.id, + }; + const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK4), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); - await writeTokenStateClientEntry( + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient3, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient4, + dynamoDBClient + ); + await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, dynamoDBClient ); @@ -2327,7 +2411,7 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -2340,10 +2424,10 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const newTokenClientPurposeEntryData = { + const newTokenConsumerClientData = { GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -2366,20 +2450,20 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient1, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, kid: kid1, }), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient2, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, @@ -2387,14 +2471,14 @@ describe("integration tests V1 events", async () => { }), }; - expect(retrievedTokenEntries).toHaveLength(5); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(5); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ tokenClientEntryWithOtherClient, - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -2436,33 +2520,36 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should do no operation if the purpose platform-states entry doesn't exist and the token-generation-states entries aren't associated to the purpose id in the message", async () => { @@ -2491,7 +2578,7 @@ describe("integration tests V1 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -2499,26 +2586,26 @@ describe("integration tests V1 events", async () => { clientId: client.id, kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toBeUndefined(); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { @@ -2557,7 +2644,10 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purposeId1, purposeId2], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const mockClientKidPurpose1 = "mockClientKidPurpose1"; @@ -2583,34 +2673,38 @@ describe("integration tests V1 events", async () => { purposeId: purposeId2, }); - const tokenClientEntry = getMockTokenStatesClientEntry(); + const tokenClientEntry = getMockTokenGenStatesApiClient(); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), - GSIPK_purposeId: purposeId1, - }; + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_purposeId: purposeId1, + }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), - GSIPK_purposeId: purposeId2, - }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_purposeId: purposeId2, + }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -2623,12 +2717,12 @@ describe("integration tests V1 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( - expect.arrayContaining([tokenClientEntry, tokenClientPurposeEntry1]) + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([tokenClientEntry, tokenConsumerClient1]) ); }); }); @@ -2677,44 +2771,47 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(clientPlatformStateEntry1, dynamoDBClient); - await writeClientEntry(clientPlatformStateEntry2, dynamoDBClient); + await writePlatformClientEntry(clientPlatformStateEntry1, dynamoDBClient); + await writePlatformClientEntry(clientPlatformStateEntry2, dynamoDBClient); // token-generation-states - const pkTokenStates1 = makeTokenGenerationStatesClientKidPurposePK({ + const pkTokenGenStates1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: "kid", purposeId, }); - const pkTokenStates2 = makeTokenGenerationStatesClientKidPurposePK({ + const pkTokenGenStates2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: otherClientId, kid: "kid", purposeId, }); - const clientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + const clientPurposeTokenGenStatesEntry: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(pkTokenStates1), + ...getMockTokenGenStatesConsumerClient(pkTokenGenStates1), GSIPK_clientId: client.id, }; - const otherClientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + const otherClientPurposeTokenGenStatesEntry: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(pkTokenStates2), + ...getMockTokenGenStatesConsumerClient(pkTokenGenStates2), GSIPK_clientId: otherClientId, }; - await writeTokenStateEntry(clientPurposeTokenStateEntry, dynamoDBClient); - await writeTokenStateEntry( - otherClientPurposeTokenStateEntry, + await writeTokenGenStatesConsumerClient( + clientPurposeTokenGenStatesEntry, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + otherClientPurposeTokenGenStatesEntry, dynamoDBClient ); await handleMessageV1(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntries = await readAllPlatformStateItems( + const retrievedPlatformStatesEntries = await readAllPlatformStatesItems( dynamoDBClient ); expect(retrievedPlatformStatesEntries).toEqual([ @@ -2722,11 +2819,11 @@ describe("integration tests V1 events", async () => { ]); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([ - otherClientPurposeTokenStateEntry, + expect(retrievedTokenGenStatesEntries).toEqual([ + otherClientPurposeTokenGenStatesEntry, ]); }); }); diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index bb8fce9f23..1cbb8c2d5d 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -14,27 +14,29 @@ import { buildDynamoDBTables, deleteDynamoDBTables, getMockClient, - getMockTokenStatesClientEntry, - getMockTokenStatesClientPurposeEntry, - readAllPlatformStateItems, - readAllTokenStateItems, + getMockTokenGenStatesApiClient, + getMockTokenGenStatesConsumerClient, + readAllPlatformStatesItems, + readAllTokenGenStatesItems, getMockPurpose, getMockPurposeVersion, writePlatformPurposeEntry, getMockAgreement, writePlatformAgreementEntry, getMockDescriptor, - writeCatalogEntry, + writePlatformCatalogEntry, getMockKey, - writeTokenStateEntry, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { + Agreement, AuthorizationEventEnvelope, Client, ClientDeletedV2, ClientId, ClientKeyAddedV2, ClientKeyDeletedV2, + clientKindTokenGenStates, ClientPurposeAddedV2, ClientPurposeRemovedV2, Descriptor, @@ -59,15 +61,15 @@ import { purposeVersionState, TenantId, toClientV2, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { clientKindToTokenGenerationStatesClientKind, - readClientEntry, - writeClientEntry, - writeTokenStateClientEntry, + readPlatformClientEntry, + writePlatformClientEntry, + writeTokenGenStatesApiClient, } from "../src/utils.js"; import { config } from "./utils.js"; @@ -130,34 +132,37 @@ describe("integration tests V2 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: key.kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should insert platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { @@ -271,7 +276,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -288,11 +293,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -316,31 +321,35 @@ describe("integration tests V2 events", async () => { clientId: client.id, purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_purposeId: purpose2.id, - }; - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -356,12 +365,12 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, + ...tokenConsumerClient1, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -390,9 +399,9 @@ describe("integration tests V2 events", async () => { }), updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, + ...tokenConsumerClient2, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -422,13 +431,13 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(4); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(4); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -545,7 +554,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -562,7 +571,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -574,7 +583,10 @@ describe("integration tests V2 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPurposePK1 = @@ -597,31 +609,35 @@ describe("integration tests V2 events", async () => { clientId: client.id, purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_purposeId: purpose2.id, - }; - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -634,12 +650,12 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, + ...tokenConsumerClient1, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -668,9 +684,9 @@ describe("integration tests V2 events", async () => { }), updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, + ...tokenConsumerClient2, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -700,13 +716,13 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(4); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(4); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -823,7 +839,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -840,7 +856,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -852,7 +868,10 @@ describe("integration tests V2 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPurposePK1 = @@ -897,51 +916,59 @@ describe("integration tests V2 events", async () => { purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_purposeId: purpose2.id, - }; - const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK3), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK4), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, - GSIPK_purposeId: purpose2.id, - }; - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_purposeId: purpose2.id, + }; + const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK3), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK4), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, + GSIPK_purposeId: purpose2.id, + }; + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient3, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient4, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -954,12 +981,12 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, + ...tokenConsumerClient1, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -988,9 +1015,9 @@ describe("integration tests V2 events", async () => { }), updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, + ...tokenConsumerClient2, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: addedKey.kid, @@ -1020,13 +1047,13 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(4); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(4); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -1058,7 +1085,7 @@ describe("integration tests V2 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -1066,18 +1093,18 @@ describe("integration tests V2 events", async () => { clientId: client.id, kid: oldKey.kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1093,24 +1120,24 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) ); }); @@ -1151,25 +1178,28 @@ describe("integration tests V2 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: oldKey.kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1182,24 +1212,24 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) ); }); @@ -1240,7 +1270,10 @@ describe("integration tests V2 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ @@ -1251,24 +1284,24 @@ describe("integration tests V2 events", async () => { clientId: client.id, kid: addedKey.kid, }); - const tokenClientEntry1: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK1), + const tokenClientEntry1: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK1), consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - const tokenClientEntry2: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK2), + const tokenClientEntry2: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK2), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), }; - await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); - await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry2, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1281,24 +1314,24 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformClientEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesClientEntry = { + const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), updatedAt: new Date().toISOString(), }; - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([tokenClientEntry1, expectedTokenClientEntry]) ); }); @@ -1341,34 +1374,37 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: kidToRemove, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kidToRemove), }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should insert platform-states entry and delete token-generation-states entries for that kid", async () => { @@ -1403,24 +1439,23 @@ describe("integration tests V2 events", async () => { purposeId, }); - const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), - GSIPK_clientId: client.id, - }; - const tokenClientPurposeEntryWithOtherKid: TokenGenerationStatesClientPurposeEntry = + const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; + const tokenConsumerClientWithOtherKid: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_clientId: client.id, }; - await writeTokenStateEntry( - tokenClientPurposeEntryWithKid, + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithKid, dynamoDBClient ); - await writeTokenStateEntry( - tokenClientPurposeEntryWithOtherKid, + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithOtherKid, dynamoDBClient ); @@ -1428,7 +1463,7 @@ describe("integration tests V2 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1444,11 +1479,11 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([ - tokenClientPurposeEntryWithOtherKid, + expect(retrievedTokenGenStatesEntries).toEqual([ + tokenConsumerClientWithOtherKid, ]); }); @@ -1489,7 +1524,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purposeId], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPurposePK = @@ -1503,24 +1541,23 @@ describe("integration tests V2 events", async () => { kid: otherKid, }); - const tokenClientPurposeEntryWithKid: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), - GSIPK_clientId: client.id, - }; + const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId: client.id, + }; - const tokenClientEntryWithOtherKid: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntryWithOtherKid: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(otherKid), }; - await writeTokenStateEntry( - tokenClientPurposeEntryWithKid, + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithKid, dynamoDBClient ); - await writeTokenStateClientEntry( + await writeTokenGenStatesApiClient( tokenClientEntryWithOtherKid, dynamoDBClient ); @@ -1528,7 +1565,7 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1541,10 +1578,12 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntryWithOtherKid]); + expect(retrievedTokenGenStatesEntries).toEqual([ + tokenClientEntryWithOtherKid, + ]); }); }); @@ -1584,32 +1623,35 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should update only platform-states entry if there are no keys in the client", async () => { @@ -1648,18 +1690,24 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purposeId], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states - const tokenClientPurposeEntry = getMockTokenStatesClientPurposeEntry(); - const tokenClientEntry = getMockTokenStatesClientEntry(); - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + const tokenConsumerClient = getMockTokenGenStatesConsumerClient(); + const tokenClientEntry = getMockTokenGenStatesApiClient(); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient, + dynamoDBClient + ); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1672,13 +1720,13 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( - expect.arrayContaining([tokenClientPurposeEntry, tokenClientEntry]) + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([tokenConsumerClient, tokenClientEntry]) ); }); @@ -1724,7 +1772,11 @@ describe("integration tests V2 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry, dynamoDBClient); - const agreement = getMockAgreement(); + const agreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose.eserviceId, + }; const platformAgreementEntry: PlatformStatesAgreementEntry = { PK: makePlatformStatesAgreementPK(agreement.id), version: 1, @@ -1754,7 +1806,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -1766,7 +1818,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const kid1 = "KID1"; @@ -1780,30 +1835,38 @@ describe("integration tests V2 events", async () => { kid: kid2, }); - const tokenClientEntry1: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK1), + const tokenClientEntry1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK1), + consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kid1), }; - const tokenClientEntry2: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK2), + const tokenClientEntry2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), + consumerId: client.consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kid2), }; - const tokenClientPurposeEntryWithOtherClient = - getMockTokenStatesClientPurposeEntry(); + const tokenConsumerClientWithOtherClient = + getMockTokenGenStatesConsumerClient(); - await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); - await writeTokenStateClientEntry(tokenClientEntry2, dynamoDBClient); - await writeTokenStateEntry( - tokenClientPurposeEntryWithOtherClient, + await writeTokenGenStatesConsumerClient( + tokenClientEntry1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenClientEntry2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClientWithOtherClient, dynamoDBClient ); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -1816,10 +1879,11 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const newTokenClientPurposeEntryData = { + const newTokenConsumerClientData = { + clientKind: clientKindTokenGenStates.consumer, GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose.id, @@ -1842,10 +1906,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { ...tokenClientEntry1, - ...newTokenClientPurposeEntryData, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose.id, @@ -1853,10 +1917,10 @@ describe("integration tests V2 events", async () => { }), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { ...tokenClientEntry2, - ...newTokenClientPurposeEntryData, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose.id, @@ -1864,12 +1928,12 @@ describe("integration tests V2 events", async () => { }), }; - expect(retrievedTokenEntries).toHaveLength(3); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(3); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenClientPurposeEntryWithOtherClient, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClientWithOtherClient, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -1983,7 +2047,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -2000,7 +2064,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -2012,7 +2076,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purpose1.id], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const kid1 = "KID1"; @@ -2034,29 +2101,33 @@ describe("integration tests V2 events", async () => { clientId: client.id, purposeId: purpose1.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); - await writeTokenStateClientEntry( + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, dynamoDBClient ); @@ -2064,7 +2135,7 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -2077,10 +2148,10 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const newTokenClientPurposeEntryData = { + const newTokenConsumerClientData = { GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -2103,10 +2174,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient1, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, @@ -2114,10 +2185,10 @@ describe("integration tests V2 events", async () => { }), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient2, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, @@ -2125,14 +2196,14 @@ describe("integration tests V2 events", async () => { }), }; - expect(retrievedTokenEntries).toHaveLength(5); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(5); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ tokenClientEntryWithOtherClient, - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -2247,7 +2318,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry1, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry1, dynamoDBClient); const descriptor2: Descriptor = { ...getMockDescriptor(), @@ -2264,7 +2335,7 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousDescriptorEntry2, dynamoDBClient); + await writePlatformCatalogEntry(previousDescriptorEntry2, dynamoDBClient); const platformClientPK = makePlatformStatesClientPK(client.id); const previousPlatformClientEntry: PlatformStatesClientEntry = { @@ -2276,7 +2347,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purpose1.id], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const kid1 = "KID1"; @@ -2310,49 +2384,57 @@ describe("integration tests V2 events", async () => { clientId: client.id, purposeId: purpose2.id, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK1), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK2), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), - GSIPK_purposeId: purpose1.id, - }; - const tokenClientPurposeEntry3: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK3), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), - GSIPK_purposeId: purpose2.id, - }; - const tokenClientPurposeEntry4: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPK4), - consumerId: client.consumerId, - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), - GSIPK_purposeId: purpose2.id, - }; - const tokenClientEntryWithOtherClient = getMockTokenStatesClientEntry(); + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK1), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose1.id, + }; + const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK3), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_purposeId: purpose2.id, + }; + const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPK4), + consumerId: client.consumerId, + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_purposeId: purpose2.id, + }; + const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry3, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry4, dynamoDBClient); - await writeTokenStateClientEntry( + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient3, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient4, + dynamoDBClient + ); + await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, dynamoDBClient ); @@ -2360,7 +2442,7 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -2373,10 +2455,10 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const newTokenClientPurposeEntryData = { + const newTokenConsumerClientData = { GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -2399,20 +2481,20 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), }; - const expectedTokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient1: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry1, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient1, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, kid: kid1, }), }; - const expectedTokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = { - ...tokenClientPurposeEntry2, - ...newTokenClientPurposeEntryData, + ...tokenConsumerClient2, + ...newTokenConsumerClientData, PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, @@ -2420,14 +2502,14 @@ describe("integration tests V2 events", async () => { }), }; - expect(retrievedTokenEntries).toHaveLength(5); - expect(retrievedTokenEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(5); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ tokenClientEntryWithOtherClient, - tokenClientPurposeEntry1, - tokenClientPurposeEntry2, - expectedTokenClientPurposeEntry1, - expectedTokenClientPurposeEntry2, + tokenConsumerClient1, + tokenConsumerClient2, + expectedTokenConsumerClient1, + expectedTokenConsumerClient2, ]) ); }); @@ -2469,33 +2551,36 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toEqual(previousPlatformClientEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should do no operation if the purpose platform-states entry doesn't exist and the token-generation-states entries aren't associated to the purpose id in the message", async () => { @@ -2524,7 +2609,7 @@ describe("integration tests V2 events", async () => { // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); expect( - await readClientEntry(platformClientPK, dynamoDBClient) + await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); // token-generation-states @@ -2533,26 +2618,26 @@ describe("integration tests V2 events", async () => { kid: "KID", }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformClientEntry = await readClientEntry( + const retrievedPlatformClientEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); expect(retrievedPlatformClientEntry).toBeUndefined(); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([tokenClientEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { @@ -2591,7 +2676,10 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [purposeId1, purposeId2], }; - await writeClientEntry(previousPlatformClientEntry, dynamoDBClient); + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient + ); // token-generation-states const mockClientKidPurpose1 = "mockClientKidPurpose1"; @@ -2617,34 +2705,38 @@ describe("integration tests V2 events", async () => { purposeId: purposeId2, }); - const tokenClientEntry = getMockTokenStatesClientEntry(); + const tokenClientEntry = getMockTokenGenStatesApiClient(); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), - GSIPK_purposeId: purposeId1, - }; + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_purposeId: purposeId1, + }; - const tokenClientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK2), - GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, - GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), - GSIPK_purposeId: purposeId2, - }; + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_purposeId: purposeId2, + }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(tokenClientPurposeEntry2, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntry = await readClientEntry( + const retrievedPlatformStatesEntry = await readPlatformClientEntry( platformClientPK, dynamoDBClient ); @@ -2657,12 +2749,12 @@ describe("integration tests V2 events", async () => { expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toHaveLength(2); - expect(retrievedTokenEntries).toEqual( - expect.arrayContaining([tokenClientEntry, tokenClientPurposeEntry1]) + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([tokenClientEntry, tokenConsumerClient1]) ); }); }); @@ -2712,44 +2804,47 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writeClientEntry(clientPlatformStateEntry1, dynamoDBClient); - await writeClientEntry(clientPlatformStateEntry2, dynamoDBClient); + await writePlatformClientEntry(clientPlatformStateEntry1, dynamoDBClient); + await writePlatformClientEntry(clientPlatformStateEntry2, dynamoDBClient); // token-generation-states - const pkTokenStates1 = makeTokenGenerationStatesClientKidPurposePK({ + const pkTokenGenStates1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: "kid", purposeId, }); - const pkTokenStates2 = makeTokenGenerationStatesClientKidPurposePK({ + const pkTokenGenStates2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: otherClientId, kid: "kid", purposeId, }); - const clientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + const clientPurposeTokenGenStatesEntry: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(pkTokenStates1), + ...getMockTokenGenStatesConsumerClient(pkTokenGenStates1), GSIPK_clientId: client.id, }; - const otherClientPurposeTokenStateEntry: TokenGenerationStatesClientPurposeEntry = + const otherClientPurposeTokenGenStatesEntry: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(pkTokenStates2), + ...getMockTokenGenStatesConsumerClient(pkTokenGenStates2), GSIPK_clientId: otherClientId, }; - await writeTokenStateEntry(clientPurposeTokenStateEntry, dynamoDBClient); - await writeTokenStateEntry( - otherClientPurposeTokenStateEntry, + await writeTokenGenStatesConsumerClient( + clientPurposeTokenGenStatesEntry, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + otherClientPurposeTokenGenStatesEntry, dynamoDBClient ); await handleMessageV2(message, dynamoDBClient); // platform-states - const retrievedPlatformStatesEntries = await readAllPlatformStateItems( + const retrievedPlatformStatesEntries = await readAllPlatformStatesItems( dynamoDBClient ); expect(retrievedPlatformStatesEntries).toEqual([ @@ -2757,11 +2852,11 @@ describe("integration tests V2 events", async () => { ]); // token-generation-states - const retrievedTokenEntries = await readAllTokenStateItems( + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenEntries).toEqual([ - otherClientPurposeTokenStateEntry, + expect(retrievedTokenGenStatesEntries).toEqual([ + otherClientPurposeTokenGenStatesEntry, ]); }); }); diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 477a2bf32c..25ef23b86b 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -4,20 +4,20 @@ import { fail } from "assert"; import { buildDynamoDBTables, deleteDynamoDBTables, - getMockAgreementEntry, + getMockPlatformStatesAgreementEntry, getMockKey, getMockPlatformStatesClientEntry, getMockPurposeVersion, - getMockTokenStatesClientEntry, - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesApiClient, + getMockTokenGenStatesConsumerClient, getMockPurpose, getMockClient, - readAllPlatformStateItems, - readAllTokenStateItems, - writeCatalogEntry, + readAllPlatformStatesItems, + readAllTokenGenStatesItems, + writePlatformCatalogEntry, writePlatformAgreementEntry, writePlatformPurposeEntry, - writeTokenStateEntry, + writeTokenGenStatesConsumerClient, getMockAgreement, getMockDescriptor, getMockEService, @@ -27,7 +27,7 @@ import { AgreementId, Client, ClientId, - clientKindTokenStates, + clientKindTokenGenStates, DescriptorId, EService, EServiceId, @@ -52,9 +52,9 @@ import { PurposeId, purposeVersionState, TenantId, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, - TokenGenerationStatesGenericEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, + TokenGenerationStatesGenericClient, unsafeBrandId, } from "pagopa-interop-models"; import { @@ -73,20 +73,20 @@ import { setClientPurposeIdsInPlatformStatesEntry, convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, - deleteClientEntryFromTokenGenerationStatesTable, - deleteEntriesFromTokenStatesByClient, - deleteEntriesFromTokenStatesByKid, - deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable, - readClientEntriesInTokenGenerationStates, - readClientEntry, + readConsumerClientEntriesInTokenGenerationStates, readPlatformAgreementEntryByGSIPKConsumerIdEServiceId, retrievePlatformStatesByPurpose, - updateTokenDataForSecondRetrieval, + updateTokenGenStatesDataForSecondRetrieval, upsertPlatformClientEntry, - upsertTokenClientKidEntry, - upsertTokenStateClientPurposeEntry, - writeClientEntry, - writeTokenStateClientEntry, + writeTokenGenStatesApiClient, + deleteEntriesFromTokenGenStatesByKid, + writePlatformClientEntry, + deleteEntriesFromTokenGenStatesByClientId, + deleteClientEntryFromTokenGenerationStates, + readPlatformClientEntry, + deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, + upsertTokenGenStatesConsumerClient, + upsertTokenGenStatesApiClient, } from "../src/utils.js"; import { config } from "./utils.js"; @@ -112,30 +112,36 @@ describe("utils", () => { vi.useRealTimers(); }); - it("deleteEntriesFromTokenStatesByKid", async () => { + it("deleteEntriesFromTokenGenStatesByKid", async () => { const kid = unsafeBrandId("mock kid"); - const clientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), + const clientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), GSIPK_kid: kid, }; - const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), GSIPK_kid: kid, }; - const otherClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const otherConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), }; - await writeTokenStateClientEntry(clientEntry, dynamoDBClient); - await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); - await writeTokenStateEntry(otherClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + otherConsumerClient, + dynamoDBClient + ); - await deleteEntriesFromTokenStatesByKid(kid, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByKid(kid, dynamoDBClient); - const result = await readAllTokenStateItems(dynamoDBClient); - expect(result).toEqual([otherClientPurposeEntry]); + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherConsumerClient]); }); it("deleteClientEntryFromPlatformStates", async () => { @@ -149,126 +155,152 @@ describe("utils", () => { ...getMockPlatformStatesClientEntry(pk2), }; - await writeClientEntry(clientEntry1, dynamoDBClient); - await writeClientEntry(clientEntry2, dynamoDBClient); + await writePlatformClientEntry(clientEntry1, dynamoDBClient); + await writePlatformClientEntry(clientEntry2, dynamoDBClient); await deleteClientEntryFromPlatformStates(pk1, dynamoDBClient); - const res = await readAllPlatformStateItems(dynamoDBClient); + const res = await readAllPlatformStatesItems(dynamoDBClient); expect(res).toEqual([clientEntry2]); }); - it("deleteEntriesFromTokenStatesByClient", async () => { + it("deleteEntriesFromTokenGenStatesByClientId", async () => { const GSIPK_clientId = generateId(); - const clientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), + const clientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), GSIPK_clientId, }; - const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), GSIPK_clientId, }; - const otherClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const otherConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), }; - await writeTokenStateClientEntry(clientEntry, dynamoDBClient); - await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); - await writeTokenStateEntry(otherClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + otherConsumerClient, + dynamoDBClient + ); - await deleteEntriesFromTokenStatesByClient(GSIPK_clientId, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByClientId( + GSIPK_clientId, + dynamoDBClient + ); - const result = await readAllTokenStateItems(dynamoDBClient); - expect(result).toEqual([otherClientPurposeEntry]); + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherConsumerClient]); }); - describe("deleteClientEntryFromTokenGenerationStatesTable", () => { - it("clientKid entry", async () => { - const clientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), + describe("deleteClientEntryFromTokenGenerationStates", () => { + it("tokenGenStatesApiClient", async () => { + const clientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), }; - const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - }; + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(), + }; - await writeTokenStateClientEntry(clientEntry, dynamoDBClient); - await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); - await deleteClientEntryFromTokenGenerationStatesTable( + await deleteClientEntryFromTokenGenerationStates( clientEntry, dynamoDBClient ); - const result = await readAllTokenStateItems(dynamoDBClient); - expect(result).toEqual([clientPurposeEntry]); + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([tokenGenStatesConsumerClient]); }); - it("clientKidPurpose entry", async () => { - const clientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), + it("tokenGenStatesConsumerClient", async () => { + const clientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), }; - const clientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - }; + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(), + }; - await writeTokenStateClientEntry(clientEntry, dynamoDBClient); - await writeTokenStateEntry(clientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); - await deleteClientEntryFromTokenGenerationStatesTable( - clientPurposeEntry, + await deleteClientEntryFromTokenGenerationStates( + tokenGenStatesConsumerClient, dynamoDBClient ); - const result = await readAllTokenStateItems(dynamoDBClient); + const result = await readAllTokenGenStatesItems(dynamoDBClient); expect(result).toEqual([clientEntry]); }); }); - it("readClientEntry", async () => { + it("readPlatformClientEntry", async () => { const clientEntry1 = getMockPlatformStatesClientEntry(); const clientEntry2 = getMockPlatformStatesClientEntry(); - await writeClientEntry(clientEntry1, dynamoDBClient); - await writeClientEntry(clientEntry2, dynamoDBClient); + await writePlatformClientEntry(clientEntry1, dynamoDBClient); + await writePlatformClientEntry(clientEntry2, dynamoDBClient); - const res = await readClientEntry(clientEntry1.PK, dynamoDBClient); + const res = await readPlatformClientEntry(clientEntry1.PK, dynamoDBClient); expect(res).toEqual(clientEntry1); }); - it("deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable", async () => { + it("deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId", async () => { const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ clientId: generateId(), purposeId: generateId(), }); - const clientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), GSIPK_clientId_purposeId, }; - const clientPurposeEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), GSIPK_clientId_purposeId, }; - const clientPurposeEntry3 = getMockTokenStatesClientPurposeEntry(); + const tokenGenStatesConsumerClient3 = getMockTokenGenStatesConsumerClient(); - await writeTokenStateEntry(clientPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(clientPurposeEntry2, dynamoDBClient); - await writeTokenStateEntry(clientPurposeEntry3, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient3, + dynamoDBClient + ); - await deleteEntriesWithClientAndPurposeFromTokenGenerationStatesTable( + await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( GSIPK_clientId_purposeId, dynamoDBClient ); - const result = await readAllTokenStateItems(dynamoDBClient); - expect(result).toEqual([clientPurposeEntry3]); + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([tokenGenStatesConsumerClient3]); }); it("convertEntriesToClientKidInTokenGenerationStates", async () => { @@ -287,8 +319,8 @@ describe("utils", () => { kid: kid1, purposeId, }); - const clientKidPurposeEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(pk1), + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(pk1), GSIPK_clientId_purposeId, GSIPK_clientId: clientId, GSIPK_kid: unsafeBrandId(kid1), @@ -299,69 +331,78 @@ describe("utils", () => { kid: kid2, purposeId, }); - const clientKidPurposeEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(pk2), + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(pk2), GSIPK_clientId_purposeId, GSIPK_clientId: clientId, GSIPK_kid: unsafeBrandId(kid2), }; - const clientKidPurposeEntry3 = getMockTokenStatesClientPurposeEntry(); + const tokenGenStatesConsumerClient3 = getMockTokenGenStatesConsumerClient(); - await writeTokenStateEntry(clientKidPurposeEntry1, dynamoDBClient); - await writeTokenStateEntry(clientKidPurposeEntry2, dynamoDBClient); - await writeTokenStateEntry(clientKidPurposeEntry3, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient3, + dynamoDBClient + ); await convertEntriesToClientKidInTokenGenerationStates( GSIPK_clientId_purposeId, dynamoDBClient ); - const expectedEntry1: TokenGenerationStatesClientEntry = { - consumerId: clientKidPurposeEntry1.consumerId, + const expectedEntry1: TokenGenerationStatesConsumerClient = { + consumerId: tokenGenStatesConsumerClient1.consumerId, updatedAt: new Date().toISOString(), PK: makeTokenGenerationStatesClientKidPK({ clientId, kid: kid1 }), - clientKind: clientKindTokenStates.consumer, - publicKey: clientKidPurposeEntry1.publicKey, + clientKind: clientKindTokenGenStates.consumer, + publicKey: tokenGenStatesConsumerClient1.publicKey, GSIPK_clientId: clientId, GSIPK_kid: unsafeBrandId(kid1), }; - const expectedEntry2: TokenGenerationStatesClientEntry = { - consumerId: clientKidPurposeEntry2.consumerId, + const expectedEntry2: TokenGenerationStatesConsumerClient = { + consumerId: tokenGenStatesConsumerClient2.consumerId, updatedAt: new Date().toISOString(), PK: makeTokenGenerationStatesClientKidPK({ clientId, kid: kid2 }), clientKind: "CONSUMER", - publicKey: clientKidPurposeEntry1.publicKey, + publicKey: tokenGenStatesConsumerClient1.publicKey, GSIPK_clientId: clientId, GSIPK_kid: unsafeBrandId(kid2), }; - const result = await readAllTokenStateItems(dynamoDBClient); + const result = await readAllTokenGenStatesItems(dynamoDBClient); expect(result).toEqual( expect.arrayContaining([ expectedEntry2, expectedEntry1, - clientKidPurposeEntry3, + tokenGenStatesConsumerClient3, ]) ); }); - describe("writeTokenStateClientEntry", () => { + describe("writeTokenGenStatesApiClient", () => { it("should succeed if the entry doesn't exist", async () => { - const clientEntry = getMockTokenStatesClientEntry(); - await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + const clientEntry = getMockTokenGenStatesApiClient(); + await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); - const result = await readAllTokenStateItems(dynamoDBClient); + const result = await readAllTokenGenStatesItems(dynamoDBClient); expect(result).toEqual([clientEntry]); }); it("should throw error if the entry already exists", async () => { - const clientEntry = getMockTokenStatesClientEntry(); - await writeTokenStateClientEntry(clientEntry, dynamoDBClient); + const clientEntry = getMockTokenGenStatesApiClient(); + await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); expect( - writeTokenStateClientEntry(clientEntry, dynamoDBClient) + writeTokenGenStatesApiClient(clientEntry, dynamoDBClient) ).rejects.toThrowError(); }); }); @@ -380,16 +421,16 @@ describe("utils", () => { threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); const agreementEntry1: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(pk1, GSIPK_consumerId_eserviceId), + ...getMockPlatformStatesAgreementEntry(pk1, GSIPK_consumerId_eserviceId), GSISK_agreementTimestamp: threeHoursAgo.toISOString(), }; const agreementEntry2: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(pk2, GSIPK_consumerId_eserviceId), + ...getMockPlatformStatesAgreementEntry(pk2, GSIPK_consumerId_eserviceId), GSISK_agreementTimestamp: new Date().toISOString(), }; - const agreementEntry3 = getMockAgreementEntry(pk3); + const agreementEntry3 = getMockPlatformStatesAgreementEntry(pk3); await writePlatformAgreementEntry(agreementEntry1, dynamoDBClient); await writePlatformAgreementEntry(agreementEntry2, dynamoDBClient); @@ -403,40 +444,45 @@ describe("utils", () => { expect(res).toEqual(agreementEntry2); }); - describe("upsertTokenStateClientPurposeEntry", () => { + describe("upsertTokenGenStatesConsumerClient", () => { it("write", async () => { - const clientKidPurposeEntry = getMockTokenStatesClientPurposeEntry(); + const tokenGenStatesConsumerClient = + getMockTokenGenStatesConsumerClient(); - const resultBefore = await readAllTokenStateItems(dynamoDBClient); + const resultBefore = await readAllTokenGenStatesItems(dynamoDBClient); expect(resultBefore).toEqual([]); - await upsertTokenStateClientPurposeEntry( - clientKidPurposeEntry, + await upsertTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, dynamoDBClient ); - const resultAfter = await readAllTokenStateItems(dynamoDBClient); - expect(resultAfter).toEqual([clientKidPurposeEntry]); + const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); + expect(resultAfter).toEqual([tokenGenStatesConsumerClient]); }); it("update", async () => { - const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - descriptorState: itemState.active, - }; - await writeTokenStateEntry(clientKidPurposeEntry, dynamoDBClient); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(), + descriptorState: itemState.active, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); - const updatedEntry: TokenGenerationStatesClientPurposeEntry = { - ...clientKidPurposeEntry, + const updatedEntry: TokenGenerationStatesConsumerClient = { + ...tokenGenStatesConsumerClient, descriptorState: itemState.inactive, }; - await upsertTokenStateClientPurposeEntry(updatedEntry, dynamoDBClient); + await upsertTokenGenStatesConsumerClient(updatedEntry, dynamoDBClient); - const resultAfter = await readAllTokenStateItems(dynamoDBClient); + const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); expect(resultAfter).toEqual([updatedEntry]); }); }); - it("readClientEntriesInTokenGenerationStates", async () => { + it("readConsumerClientEntriesInTokenGenerationStates", async () => { const clientId = generateId(); const pk1 = makeTokenGenerationStatesClientKidPK({ clientId, kid: "" }); const pk2 = makeTokenGenerationStatesClientKidPurposePK({ @@ -447,26 +493,37 @@ describe("utils", () => { const GSIPK_clientId = clientId; - const clientKidEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(pk1), - GSIPK_clientId, - }; + const tokenGenStatesConsumerClientWithoutPurpose: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(pk1), + GSIPK_clientId, + }; - const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(pk2), - GSIPK_clientId, - }; + const tokenGenStatesConsumerClientWithPurpose: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(pk2), + GSIPK_clientId, + }; - await writeTokenStateClientEntry(clientKidEntry, dynamoDBClient); - await writeTokenStateEntry(clientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClientWithoutPurpose, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClientWithPurpose, + dynamoDBClient + ); - const res = await readClientEntriesInTokenGenerationStates( + const res = await readConsumerClientEntriesInTokenGenerationStates( GSIPK_clientId, dynamoDBClient ); expect(res).toEqual( - expect.arrayContaining([clientKidEntry, clientKidPurposeEntry]) + expect.arrayContaining([ + tokenGenStatesConsumerClientWithoutPurpose, + tokenGenStatesConsumerClientWithPurpose, + ]) ); }); @@ -480,8 +537,8 @@ describe("utils", () => { clientPurposesIds: [generateId(), generateId()], }; - await writeClientEntry(clientEntry1, dynamoDBClient); - await writeClientEntry(clientEntry2, dynamoDBClient); + await writePlatformClientEntry(clientEntry1, dynamoDBClient); + await writePlatformClientEntry(clientEntry2, dynamoDBClient); await setClientPurposeIdsInPlatformStatesEntry( { @@ -492,7 +549,7 @@ describe("utils", () => { dynamoDBClient ); - const res = await readAllPlatformStateItems(dynamoDBClient); + const res = await readAllPlatformStatesItems(dynamoDBClient); expect(res).toEqual( expect.arrayContaining([ { @@ -527,7 +584,7 @@ describe("utils", () => { const agreementPK = makePlatformStatesAgreementPK(agreementId); const agreementEntry: PlatformStatesAgreementEntry = { - ...getMockAgreementEntry(agreementPK), + ...getMockPlatformStatesAgreementEntry(agreementPK), agreementDescriptorId: descriptorId, GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ consumerId, @@ -550,7 +607,7 @@ describe("utils", () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); const res = await retrievePlatformStatesByPurpose( purposeId, @@ -568,87 +625,93 @@ describe("utils", () => { it("write", async () => { const clientEntry: PlatformStatesClientEntry = { ...getMockPlatformStatesClientEntry(), - clientKind: clientKindTokenStates.consumer, + clientKind: clientKindTokenGenStates.consumer, }; - const resultBefore = await readAllPlatformStateItems(dynamoDBClient); + const resultBefore = await readAllPlatformStatesItems(dynamoDBClient); expect(resultBefore).toEqual([]); await upsertPlatformClientEntry(clientEntry, dynamoDBClient); - const resultAfter = await readAllPlatformStateItems(dynamoDBClient); + const resultAfter = await readAllPlatformStatesItems(dynamoDBClient); expect(resultAfter).toEqual([clientEntry]); }); it("update", async () => { const clientEntry: PlatformStatesClientEntry = { ...getMockPlatformStatesClientEntry(), - clientKind: clientKindTokenStates.consumer, + clientKind: clientKindTokenGenStates.consumer, }; - await writeClientEntry(clientEntry, dynamoDBClient); + await writePlatformClientEntry(clientEntry, dynamoDBClient); const updatedEntry: PlatformStatesClientEntry = { ...clientEntry, - clientKind: clientKindTokenStates.api, + clientKind: clientKindTokenGenStates.api, }; await upsertPlatformClientEntry(updatedEntry, dynamoDBClient); - const resultAfter = await readAllPlatformStateItems(dynamoDBClient); + const resultAfter = await readAllPlatformStatesItems(dynamoDBClient); expect(resultAfter).toEqual([updatedEntry]); }); }); - describe("upsertTokenClientKidEntry", () => { + describe("upsertTokenApiClient", () => { it("write", async () => { - const clientKidEntry = getMockTokenStatesClientEntry(); + const tokenGenStatesApiClient = getMockTokenGenStatesApiClient(); - const resultBefore = await readAllTokenStateItems(dynamoDBClient); + const resultBefore = await readAllTokenGenStatesItems(dynamoDBClient); expect(resultBefore).toEqual([]); - await upsertTokenClientKidEntry(clientKidEntry, dynamoDBClient); + await upsertTokenGenStatesApiClient( + tokenGenStatesApiClient, + dynamoDBClient + ); - const resultAfter = await readAllTokenStateItems(dynamoDBClient); - expect(resultAfter).toEqual([clientKidEntry]); + const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); + expect(resultAfter).toEqual([tokenGenStatesApiClient]); }); it("update", async () => { - const clientKidEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), - clientKind: clientKindTokenStates.consumer, + const tokenGenStatesApiClient: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + clientKind: clientKindTokenGenStates.api, }; - await writeTokenStateClientEntry(clientKidEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenGenStatesApiClient, + dynamoDBClient + ); - const updatedEntry: TokenGenerationStatesClientEntry = { - ...clientKidEntry, - clientKind: clientKindTokenStates.api, + const updatedEntry: TokenGenerationStatesApiClient = { + ...tokenGenStatesApiClient, + clientKind: clientKindTokenGenStates.api, }; - await upsertTokenClientKidEntry(updatedEntry, dynamoDBClient); + await upsertTokenGenStatesApiClient(updatedEntry, dynamoDBClient); - const resultAfter = await readAllTokenStateItems(dynamoDBClient); + const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); expect(resultAfter).toEqual([updatedEntry]); }); }); - it("parsing TokenGenerationStatesGenericEntry", () => { - const clientKidEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), + it("parsing TokenGenerationStatesGenericClient", () => { + const tokenGenStatesApiClient: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), }; const entries1 = z - .array(TokenGenerationStatesGenericEntry) - .safeParse([clientKidEntry]); + .array(TokenGenerationStatesGenericClient) + .safeParse([tokenGenStatesApiClient]); - expect(entries1.data![0]).toEqual(clientKidEntry); + expect(entries1.data![0]).toEqual(tokenGenStatesApiClient); - const clientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), }; const entries2 = z - .array(TokenGenerationStatesGenericEntry) - .safeParse([clientKidPurposeEntry]); + .array(TokenGenerationStatesGenericClient) + .safeParse([tokenGenStatesConsumerClient]); - expect(entries2.data![0]).toEqual(clientKidPurposeEntry); + expect(entries2.data![0]).toEqual(tokenGenStatesConsumerClient); const clientId = generateId(); - const clientKidPurposeEntryWithUndefined: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClientWithUndefined: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId, @@ -656,17 +719,19 @@ describe("utils", () => { purposeId: generateId(), }), consumerId: generateId(), - clientKind: clientKindTokenStates.api, + clientKind: clientKindTokenGenStates.consumer, publicKey: "publicKey", GSIPK_clientId: generateId(), GSIPK_kid: unsafeBrandId("kid"), updatedAt: new Date().toISOString(), }; const entries3 = z - .array(TokenGenerationStatesGenericEntry) - .safeParse([clientKidPurposeEntryWithUndefined]); + .array(TokenGenerationStatesGenericClient) + .safeParse([tokenGenStatesConsumerClientWithUndefined]); - expect(entries3.data![0]).toEqual(clientKidPurposeEntryWithUndefined); + expect(entries3.data![0]).toEqual( + tokenGenStatesConsumerClientWithUndefined + ); }); it("updateTokenDataForSecondRetrieval", async () => { @@ -702,8 +767,8 @@ describe("utils", () => { purposeId: purpose.id, } ); - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + const tokenConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), consumerId, GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), @@ -723,7 +788,10 @@ describe("utils", () => { descriptorId: descriptor.id, }), }; - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient, + dynamoDBClient + ); const platformPurposeEntry: PlatformStatesPurposeEntry = { PK: makePlatformStatesPurposePK(purpose.id), @@ -760,23 +828,26 @@ describe("utils", () => { updatedAt: new Date().toISOString(), }; - await updateTokenDataForSecondRetrieval({ + await updateTokenGenStatesDataForSecondRetrieval({ dynamoDBClient, - entry: tokenClientPurposeEntry, + entry: tokenConsumerClient, purposeEntry: platformPurposeEntry, agreementEntry: platformAgreementEntry, catalogEntry: platformCatalogEntry, }); - const retrievedTokenEntries = await readAllTokenStateItems(dynamoDBClient); - const expectedTokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...tokenClientPurposeEntry, - purposeState: itemState.inactive, - agreementState: itemState.inactive, - descriptorState: itemState.inactive, - }; - expect(retrievedTokenEntries).toHaveLength(1); - expect(retrievedTokenEntries[0]).toEqual(expectedTokenClientPurposeEntry); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + const expectedTokenConsumerClient: TokenGenerationStatesConsumerClient = { + ...tokenConsumerClient, + purposeState: itemState.inactive, + agreementState: itemState.inactive, + descriptorState: itemState.inactive, + }; + expect(retrievedTokenGenStatesEntries).toHaveLength(1); + expect(retrievedTokenGenStatesEntries[0]).toEqual( + expectedTokenConsumerClient + ); }); }); diff --git a/packages/authorization-server/src/model/domain/errors.ts b/packages/authorization-server/src/model/domain/errors.ts index f263765cb1..e646fc024c 100644 --- a/packages/authorization-server/src/model/domain/errors.ts +++ b/packages/authorization-server/src/model/domain/errors.ts @@ -1,7 +1,6 @@ import { ApiError, ClientId, - ClientKindTokenStates, makeApiProblemBuilder, TokenGenerationStatesClientKidPK, TokenGenerationStatesClientKidPurposePK, @@ -14,10 +13,8 @@ export const errorCodes = { kafkaAuditingFailed: "0004", fallbackAuditFailed: "0005", tokenGenerationStatesEntryNotFound: "0006", - invalidTokenClientKidPurposeEntry: "0007", - keyTypeMismatch: "0008", - unexpectedTokenGenerationStatesEntry: "0009", - platformStateValidationFailed: "0010", + incompleteTokenGenerationStatesConsumerClient: "0007", + platformStateValidationFailed: "0008", }; export type ErrorCodes = keyof typeof errorCodes; @@ -80,36 +77,13 @@ export function tokenGenerationStatesEntryNotFound( }); } -export function invalidTokenClientKidPurposeEntry( +export function incompleteTokenGenerationStatesConsumerClient( pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK ): ApiError { return new ApiError({ - detail: `Missing data in client-kid-purpose entry from token-generation-states table. Primary key: ${pk}`, - code: "invalidTokenClientKidPurposeEntry", - title: "Invalid token client-kid-purpose entry", - }); -} - -export function keyTypeMismatch( - pk: - | TokenGenerationStatesClientKidPurposePK - | TokenGenerationStatesClientKidPK, - clientKind: ClientKindTokenStates -): ApiError { - return new ApiError({ - detail: `Token-generation entry ${pk} can't have client kind: ${clientKind}`, - code: "keyTypeMismatch", - title: "Key type mismatch", - }); -} - -export function unexpectedTokenGenerationStatesEntry( - pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK -): ApiError { - return new ApiError({ - detail: `Unexpected token-generation-states entry, primary key: ${pk}`, - code: "unexpectedTokenGenerationStatesEntry", - title: "Unexpected token-generation-states entry", + detail: `Missing data in token-generation-states consumer client entry. Primary key: ${pk}`, + code: "incompleteTokenGenerationStatesConsumerClient", + title: "Incomplete token-generation-states consumer client entry", }); } diff --git a/packages/authorization-server/src/services/tokenService.ts b/packages/authorization-server/src/services/tokenService.ts index bb04a024e1..39586c0185 100644 --- a/packages/authorization-server/src/services/tokenService.ts +++ b/packages/authorization-server/src/services/tokenService.ts @@ -6,9 +6,7 @@ import { } from "pagopa-interop-client-assertion-validation"; import { authorizationServerApi } from "pagopa-interop-api-clients"; import { - clientKidPrefix, - clientKidPurposePrefix, - clientKindTokenStates, + clientKindTokenGenStates, DescriptorId, EServiceId, generateId, @@ -16,16 +14,15 @@ import { makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, TenantId, - TokenGenerationStatesClientEntry, + TokenGenerationStatesApiClient, TokenGenerationStatesClientKidPK, TokenGenerationStatesClientKidPurposePK, - TokenGenerationStatesClientPurposeEntry, - TokenGenerationStatesGenericEntry, + TokenGenerationStatesGenericClient, unsafeBrandId, GeneratedTokenAuditDetails, GSIPKEServiceIdDescriptorId, ClientAssertion, - FullTokenGenerationStatesClientPurposeEntry, + FullTokenGenerationStatesConsumerClient, CorrelationId, } from "pagopa-interop-models"; import { @@ -54,11 +51,9 @@ import { clientAssertionSignatureValidationFailed, clientAssertionValidationFailed, fallbackAuditFailed, - invalidTokenClientKidPurposeEntry, + incompleteTokenGenerationStatesConsumerClient, kafkaAuditingFailed, tokenGenerationStatesEntryNotFound, - keyTypeMismatch, - unexpectedTokenGenerationStatesEntry, platformStateValidationFailed, } from "../model/domain/errors.js"; @@ -159,23 +154,22 @@ export function tokenServiceBuilder({ }; } - return await match(key.clientKind) - .with(clientKindTokenStates.consumer, async () => { - const parsedKey = - FullTokenGenerationStatesClientPurposeEntry.safeParse(key); - if (parsedKey.success) { + return await match(key) + .with( + { clientKind: clientKindTokenGenStates.consumer }, + async (key) => { const token = await tokenGenerator.generateInteropConsumerToken({ sub: jwt.payload.sub, - audience: parsedKey.data.descriptorAudience, - purposeId: parsedKey.data.GSIPK_purposeId, - tokenDurationInSeconds: parsedKey.data.descriptorVoucherLifespan, + audience: key.descriptorAudience, + purposeId: key.GSIPK_purposeId, + tokenDurationInSeconds: key.descriptorVoucherLifespan, digest: jwt.payload.digest, }); await publishAudit({ producer, generatedToken: token, - key: parsedKey.data, + key, clientAssertion: jwt, correlationId, fileManager, @@ -188,9 +182,8 @@ export function tokenServiceBuilder({ rateLimiterStatus, }; } - throw invalidTokenClientKidPurposeEntry(key.PK); - }) - .with(clientKindTokenStates.api, async () => { + ) + .with({ clientKind: clientKindTokenGenStates.api }, async (key) => { const token = await tokenGenerator.generateInteropApiToken({ sub: jwt.payload.sub, consumerId: key.consumerId, @@ -211,7 +204,7 @@ export const retrieveKey = async ( dynamoDBClient: DynamoDBClient, pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK ): Promise< - TokenGenerationStatesClientEntry | TokenGenerationStatesClientPurposeEntry + FullTokenGenerationStatesConsumerClient | TokenGenerationStatesApiClient > => { const input: GetItemInput = { Key: { @@ -227,63 +220,29 @@ export const retrieveKey = async ( throw tokenGenerationStatesEntryNotFound(pk); } else { const unmarshalled = unmarshall(data.Item); - const tokenGenerationEntry = - TokenGenerationStatesGenericEntry.safeParse(unmarshalled); + const tokenGenStatesClient = + TokenGenerationStatesGenericClient.safeParse(unmarshalled); - if (!tokenGenerationEntry.success) { + if (!tokenGenStatesClient.success) { throw genericInternalError( - `Unable to parse token generation entry item: result ${JSON.stringify( - tokenGenerationEntry + `Unable to parse token-generation-states client: result ${JSON.stringify( + tokenGenStatesClient )} - data ${JSON.stringify(data)} ` ); } - return match(tokenGenerationEntry.data) - .when( - (entry) => - entry.clientKind === clientKindTokenStates.consumer && - entry.PK.startsWith(clientKidPurposePrefix), - () => { - const clientKidPurposeEntry = - FullTokenGenerationStatesClientPurposeEntry.safeParse( - tokenGenerationEntry.data - ); - if (!clientKidPurposeEntry.success) { - throw invalidTokenClientKidPurposeEntry( - tokenGenerationEntry.data.PK - ); - } - - return clientKidPurposeEntry.data; - } - ) - .when( - (entry) => - entry.clientKind === clientKindTokenStates.consumer && - entry.PK.startsWith(clientKidPrefix), - (entry) => { - throw keyTypeMismatch(entry.PK, entry.clientKind); + return match(tokenGenStatesClient.data) + .with({ clientKind: clientKindTokenGenStates.consumer }, (entry) => { + const tokenGenStatesConsumerClient = + FullTokenGenerationStatesConsumerClient.safeParse(entry); + if (!tokenGenStatesConsumerClient.success) { + throw incompleteTokenGenerationStatesConsumerClient(entry.PK); } - ) - .when( - (entry) => - entry.clientKind === clientKindTokenStates.api && - entry.PK.startsWith(clientKidPurposePrefix), - (entry) => { - throw keyTypeMismatch(entry.PK, entry.clientKind); - } - ) - .when( - (entry) => - entry.clientKind === clientKindTokenStates.api && - entry.PK.startsWith(clientKidPrefix), - () => tokenGenerationEntry.data as TokenGenerationStatesClientEntry - ) - .otherwise(() => { - throw unexpectedTokenGenerationStatesEntry( - tokenGenerationEntry.data.PK - ); - }); + + return tokenGenStatesConsumerClient.data; + }) + .with({ clientKind: clientKindTokenGenStates.api }, (entry) => entry) + .exhaustive(); } }; @@ -297,8 +256,8 @@ export const publishAudit = async ({ logger, }: { producer: Awaited>; - generatedToken: InteropConsumerToken | InteropApiToken; - key: FullTokenGenerationStatesClientPurposeEntry; + generatedToken: InteropConsumerToken; + key: FullTokenGenerationStatesConsumerClient; clientAssertion: ClientAssertion; correlationId: CorrelationId; fileManager: FileManager; diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts index aaee1da260..5fcbb1204b 100644 --- a/packages/authorization-server/test/authorizationServer.integration.test.ts +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -6,18 +6,18 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { buildDynamoDBTables, deleteDynamoDBTables, - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, getMockPurpose, getMockPurposeVersion, - getMockTokenStatesClientEntry, - writeTokenStateEntry, - writeTokenStateClientEntry, + getMockTokenGenStatesApiClient, + writeTokenGenStatesConsumerClient, + writeTokenGenStatesApiClient, getMockClientAssertion, } from "pagopa-interop-commons-test"; import { AgreementId, ClientId, - clientKindTokenStates, + clientKindTokenGenStates, EServiceId, GeneratedTokenAuditDetails, generateId, @@ -28,8 +28,8 @@ import { Purpose, PurposeId, purposeVersionState, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, unsafeBrandId, } from "pagopa-interop-models"; import { formatDateyyyyMMdd, genericLogger } from "pagopa-interop-commons"; @@ -40,12 +40,10 @@ import { clientAssertionSignatureValidationFailed, clientAssertionValidationFailed, fallbackAuditFailed, - invalidTokenClientKidPurposeEntry, - keyTypeMismatch, + incompleteTokenGenerationStatesConsumerClient, platformStateValidationFailed, tokenGenerationStatesEntryNotFound, } from "../src/model/domain/errors.js"; -import { inactiveEService } from "../../client-assertion-validation/dist/errors.js"; import { configTokenGenerationStates, dynamoDBClient, @@ -126,7 +124,7 @@ describe("authorization server tests", () => { ).rejects.toThrowError(tokenGenerationStatesEntryNotFound(entryPK)); }); - it("should throw invalidTokenClientKidPurposeEntry", async () => { + it("should throw incompleteTokenGenerationStatesConsumerClient", async () => { const purposeId = generateId(); const clientId = generateId(); @@ -149,20 +147,25 @@ describe("authorization server tests", () => { } ); - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), agreementId: undefined, }; - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); expect( tokenService.generateToken(request, generateId(), genericLogger) ).rejects.toThrowError( - invalidTokenClientKidPurposeEntry(tokenClientPurposeEntry.PK) + incompleteTokenGenerationStatesConsumerClient( + tokenGenStatesConsumerClient.PK + ) ); }); - it("should throw keyTypeMismatch - clientKid entry with consumer kind", async () => { + it("should throw incompleteTokenGenerationStatesConsumerClient - clientKid entry with consumer kind", async () => { const clientId = generateId(); const { jws, clientAssertion } = await getMockClientAssertion({ standardClaimsOverride: { sub: clientId }, @@ -179,55 +182,22 @@ describe("authorization server tests", () => { kid: clientAssertion.header.kid!, }); - const tokenClientKidEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), - clientKind: clientKindTokenStates.consumer, + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), + clientKind: clientKindTokenGenStates.consumer, }; - await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); - - expect( - tokenService.generateToken(request, generateId(), genericLogger) - ).rejects.toThrowError( - keyTypeMismatch(tokenClientKidEntry.PK, clientKindTokenStates.consumer) + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient ); - }); - - it("should throw keyTypeMismatch - clientKidPurpose entry with api kind", async () => { - const purposeId = generateId(); - const clientId = generateId(); - - const { jws, clientAssertion } = await getMockClientAssertion({ - standardClaimsOverride: { sub: clientId }, - customClaims: { purposeId }, - }); - - const request: authorizationServerApi.AccessTokenRequest = { - ...(await getMockAccessTokenRequest()), - client_assertion: jws, - client_id: clientId, - }; - - const tokenClientKidPurposePK = makeTokenGenerationStatesClientKidPurposePK( - { - clientId, - kid: clientAssertion.header.kid!, - purposeId, - } - ); - - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - clientKind: clientKindTokenStates.api, - }; - - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); expect( tokenService.generateToken(request, generateId(), genericLogger) ).rejects.toThrowError( - keyTypeMismatch(tokenClientKidPurposeEntry.PK, clientKindTokenStates.api) + incompleteTokenGenerationStatesConsumerClient( + tokenGenStatesConsumerClient.PK + ) ); }); @@ -257,10 +227,13 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK); + const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = + getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK); - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientKidPurposeEntry, + dynamoDBClient + ); expect( tokenService.generateToken(request, generateId(), genericLogger) @@ -293,19 +266,22 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - descriptorState: itemState.inactive, - publicKey: publicKeyEncodedPem, - }; + const descriptorState = itemState.inactive; + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + descriptorState, + publicKey: publicKeyEncodedPem, + }; - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); expect( tokenService.generateToken(request, generateId(), genericLogger) ).rejects.toThrowError( - platformStateValidationFailed([inactiveEService().detail]) + platformStateValidationFailed([`E-Service state is: ${descriptorState}`]) ); }); @@ -333,13 +309,15 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - publicKey: publicKeyEncodedPem, - }; + const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientKidPurposeEntry, + dynamoDBClient + ); // eslint-disable-next-line functional/no-let for (let i = 0; i < config.rateLimiterMaxRequests; i++) { const response = await tokenService.generateToken( @@ -403,14 +381,16 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - clientKind: clientKindTokenStates.consumer, - publicKey: publicKeyEncodedPem, - }; + const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + clientKind: clientKindTokenGenStates.consumer, + publicKey: publicKeyEncodedPem, + }; - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientKidPurposeEntry, + dynamoDBClient + ); expect( tokenService.generateToken(request, generateId(), genericLogger) @@ -446,13 +426,13 @@ describe("authorization server tests", () => { kid: clientAssertion.header.kid!, }); - const tokenClientKidEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), - clientKind: clientKindTokenStates.api, + const tokenClientKidEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), + clientKind: clientKindTokenGenStates.api, publicKey: publicKeyEncodedPem, }; - await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientKidEntry, dynamoDBClient); expect( tokenService.generateToken(request, generateId(), genericLogger) @@ -494,13 +474,15 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - publicKey: publicKeyEncodedPem, - }; + const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientKidPurposeEntry, + dynamoDBClient + ); expect( tokenService.generateToken(request, generateId(), genericLogger) @@ -533,13 +515,15 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - publicKey: publicKeyEncodedPem, - }; + const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; - await writeTokenStateEntry(tokenClientKidPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientKidPurposeEntry, + dynamoDBClient + ); const fileListBeforeAudit = await fileManager.listFiles( config.s3Bucket, @@ -656,8 +640,8 @@ describe("authorization server tests", () => { purposeId: purpose.id, } ); - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + const tokenClientPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), consumerId: purpose.consumerId, GSIPK_purposeId: purpose.id, purposeState: itemState.active, @@ -669,7 +653,10 @@ describe("authorization server tests", () => { publicKey: publicKeyEncodedPem, }; - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientPurposeEntry, + dynamoDBClient + ); const request = { ...(await getMockAccessTokenRequest()), @@ -770,13 +757,13 @@ describe("authorization server tests", () => { kid: clientAssertion.header.kid!, }); - const tokenClientKidEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidK), - clientKind: clientKindTokenStates.api, + const tokenClientKidEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidK), + clientKind: clientKindTokenGenStates.api, publicKey: publicKeyEncodedPem, }; - await writeTokenStateClientEntry(tokenClientKidEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientKidEntry, dynamoDBClient); const fileListBefore = await fileManager.listFiles( config.s3Bucket, diff --git a/packages/authorization-server/test/authorizationServer.unit.test.ts b/packages/authorization-server/test/authorizationServer.unit.test.ts index 80c63c68e9..bfb84d7c6b 100644 --- a/packages/authorization-server/test/authorizationServer.unit.test.ts +++ b/packages/authorization-server/test/authorizationServer.unit.test.ts @@ -3,29 +3,28 @@ import { buildDynamoDBTables, deleteDynamoDBTables, - getMockTokenStatesClientEntry, - getMockTokenStatesClientPurposeEntry, - writeTokenStateClientEntry, - writeTokenStateEntry, + getMockTokenGenStatesApiClient, + getMockTokenGenStatesConsumerClient, + writeTokenGenStatesApiClient, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { ClientId, - clientKindTokenStates, + clientKindTokenGenStates, generateId, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, PurposeId, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import {} from "pagopa-interop-client-assertion-validation"; import { genericLogger } from "pagopa-interop-commons"; import { fallbackAudit, retrieveKey } from "../src/services/tokenService.js"; import { fallbackAuditFailed, - invalidTokenClientKidPurposeEntry, - keyTypeMismatch, + incompleteTokenGenerationStatesConsumerClient, tokenGenerationStatesEntryNotFound, } from "../src/model/domain/errors.js"; import { config } from "../src/config/config.js"; @@ -71,10 +70,13 @@ describe("unit tests", () => { purposeId: purposeId2, }); - const tokenClientPurposeEntry1: TokenGenerationStatesClientPurposeEntry = - getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK1); + const tokenClientPurposeEntry1: TokenGenerationStatesConsumerClient = + getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1); - await writeTokenStateEntry(tokenClientPurposeEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientPurposeEntry1, + dynamoDBClient + ); expect( retrieveKey(dynamoDBClient, tokenClientKidPurposePK2) @@ -98,10 +100,10 @@ describe("unit tests", () => { kid, }); - const tokenClientEntry1: TokenGenerationStatesClientEntry = - getMockTokenStatesClientEntry(tokenClientKidPK1); + const tokenClientEntry1: TokenGenerationStatesApiClient = + getMockTokenGenStatesApiClient(tokenClientKidPK1); - await writeTokenStateClientEntry(tokenClientEntry1, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); expect( retrieveKey(dynamoDBClient, tokenClientKidPK2) @@ -122,16 +124,21 @@ describe("unit tests", () => { purposeId, }); - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), + const tokenClientPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), agreementId: undefined, }; - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientPurposeEntry, + dynamoDBClient + ); expect( retrieveKey(dynamoDBClient, tokenClientKidPurposePK) ).rejects.toThrowError( - invalidTokenClientKidPurposeEntry(tokenClientPurposeEntry.PK) + incompleteTokenGenerationStatesConsumerClient( + tokenClientPurposeEntry.PK + ) ); }); @@ -147,18 +154,21 @@ describe("unit tests", () => { purposeId, }); - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - clientKind: clientKindTokenStates.consumer, + const tokenClientPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + clientKind: clientKindTokenGenStates.consumer, }; - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenClientPurposeEntry, + dynamoDBClient + ); const key = await retrieveKey(dynamoDBClient, tokenClientKidPurposePK); expect(key).toEqual(tokenClientPurposeEntry); }); - it("should throw keyTypeMismatch - clientKid entry with consumer key", async () => { + it("should throw incompleteTokenGenerationStatesConsumerClient - clientKid entry with consumer key", async () => { const clientId = generateId(); const kid = "kid"; @@ -167,41 +177,16 @@ describe("unit tests", () => { kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), - clientKind: clientKindTokenStates.consumer, + const tokenClientEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), + clientKind: clientKindTokenGenStates.consumer, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient(tokenClientEntry, dynamoDBClient); expect( retrieveKey(dynamoDBClient, tokenClientKidPK) ).rejects.toThrowError( - keyTypeMismatch(tokenClientEntry.PK, clientKindTokenStates.consumer) - ); - }); - - it("should throw keyTypeMismatch - clientKidPurpose entry with api key", async () => { - const clientId = generateId(); - const kid = "kid"; - const purposeId = generateId(); - - const tokenClientKidPurposePK = - makeTokenGenerationStatesClientKidPurposePK({ - clientId, - kid, - purposeId, - }); - - const tokenClientPurposeEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenClientKidPurposePK), - clientKind: clientKindTokenStates.api, - }; - - await writeTokenStateEntry(tokenClientPurposeEntry, dynamoDBClient); - expect( - retrieveKey(dynamoDBClient, tokenClientKidPurposePK) - ).rejects.toThrowError( - keyTypeMismatch(tokenClientPurposeEntry.PK, clientKindTokenStates.api) + incompleteTokenGenerationStatesConsumerClient(tokenClientEntry.PK) ); }); @@ -214,12 +199,12 @@ describe("unit tests", () => { kid, }); - const tokenClientEntry: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(tokenClientKidPK), - clientKind: clientKindTokenStates.api, + const tokenClientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(tokenClientKidPK), + clientKind: clientKindTokenGenStates.api, }; - await writeTokenStateClientEntry(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); const key = await retrieveKey(dynamoDBClient, tokenClientKidPK); expect(key).toEqual(tokenClientEntry); diff --git a/packages/backend-for-frontend/src/services/toolService.ts b/packages/backend-for-frontend/src/services/toolService.ts index d6e851a0ea..4044432683 100644 --- a/packages/backend-for-frontend/src/services/toolService.ts +++ b/packages/backend-for-frontend/src/services/toolService.ts @@ -25,8 +25,7 @@ import { makeTokenGenerationStatesClientKidPurposePK, PurposeId, TenantId, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesGenericClient, unsafeBrandId, } from "pagopa-interop-models"; import { WithLogger } from "pagopa-interop-commons"; @@ -220,9 +219,7 @@ async function retrieveKeyAndEservice( ctx: WithLogger ): Promise< | SuccessfulValidation<{ - key: - | TokenGenerationStatesClientEntry - | TokenGenerationStatesClientPurposeEntry; + key: TokenGenerationStatesGenericClient; eservice?: catalogApi.EService; descriptor?: catalogApi.EServiceDescriptor; }> diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index a66c1dbb13..0412ded349 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -7,7 +7,7 @@ import { ItemState, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { AttributeValue, @@ -185,12 +185,12 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, descriptorState: ItemState, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Descriptor", @@ -210,14 +210,14 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } @@ -225,14 +225,14 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( await updateDescriptorStateInTokenGenerationStatesEntries( descriptorState, dynamoDBClient, - tokenStateEntries.data + tokenGenStatesEntries.data ); if (!data.LastEvaluatedKey) { - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } else { return [ - ...tokenStateEntries.data, + ...tokenGenStatesEntries.data, ...(await runPaginatedQuery( eserviceId_descriptorId, dynamoDBClient, @@ -256,12 +256,12 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( descriptorVoucherLifespan: number, descriptorAudience: string[], dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Descriptor", @@ -281,14 +281,14 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } @@ -298,14 +298,14 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( descriptorVoucherLifespan, descriptorAudience, dynamoDBClient, - entriesToUpdate: tokenStateEntries.data, + entriesToUpdate: tokenGenStatesEntries.data, }); if (!data.LastEvaluatedKey) { - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } else { return [ - ...tokenStateEntries.data, + ...tokenGenStatesEntries.data, ...(await runPaginatedQuery( eserviceId_descriptorId, dynamoDBClient, @@ -353,14 +353,14 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } @@ -368,7 +368,7 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = await updateDescriptorVoucherLifespanInTokenGenerationStatesEntries( voucherLifespan, dynamoDBClient, - tokenStateEntries.data + tokenGenStatesEntries.data ); if (data.LastEvaluatedKey) { @@ -387,7 +387,7 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = const updateDescriptorStateInTokenGenerationStatesEntries = async ( descriptorState: ItemState, dynamoDBClient: DynamoDBClient, - entriesToUpdate: TokenGenerationStatesClientPurposeEntry[] + entriesToUpdate: TokenGenerationStatesConsumerClient[] ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -426,7 +426,7 @@ const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ descriptorVoucherLifespan: number; descriptorAudience: string[]; dynamoDBClient: DynamoDBClient; - entriesToUpdate: TokenGenerationStatesClientPurposeEntry[]; + entriesToUpdate: TokenGenerationStatesConsumerClient[]; }): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -465,7 +465,7 @@ const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( voucherLifespan: number, dynamoDBClient: DynamoDBClient, - entriesToUpdate: TokenGenerationStatesClientPurposeEntry[] + entriesToUpdate: TokenGenerationStatesConsumerClient[] ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts index 74cc5351c4..f72ee94c0c 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts @@ -16,7 +16,7 @@ import { EServiceDescriptorUpdatedV1, EServiceEventEnvelope, PlatformStatesCatalogEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, descriptorState, generateId, itemState, @@ -29,13 +29,13 @@ import { getMockDescriptor, getMockEService, getMockDocument, - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, buildDynamoDBTables, deleteDynamoDBTables, - readTokenStateEntriesByEserviceIdAndDescriptorId, + readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId, } from "pagopa-interop-commons-test"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; -import { writeTokenStateEntry } from "pagopa-interop-commons-test"; +import { writeTokenGenStatesConsumerClient } from "pagopa-interop-commons-test"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { config } from "./utils.js"; @@ -91,37 +91,45 @@ describe("V1 events", async () => { }; // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -144,27 +152,27 @@ describe("V1 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -210,37 +218,45 @@ describe("V1 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -256,27 +272,27 @@ describe("V1 events", async () => { expect(retrievedCatalogEntry).toEqual(expectedCatalogEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -323,37 +339,45 @@ describe("V1 events", async () => { await writeCatalogEntry(previousCatalogStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -364,15 +388,15 @@ describe("V1 events", async () => { expect(retrievedCatalogEntry).toEqual(previousCatalogStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -421,7 +445,7 @@ describe("V1 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = + const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: generateId(), kid: `kid ${Math.random()}`, @@ -431,29 +455,35 @@ describe("V1 events", async () => { eserviceId: eservice.id, descriptorId: suspendedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = + const tokenGenStatesEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: generateId(), kid: `kid ${Math.random()}`, purposeId: generateId(), }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); const retrievedEntry = await readCatalogEntry( @@ -468,27 +498,27 @@ describe("V1 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -536,13 +566,13 @@ describe("V1 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = + const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: generateId(), kid: `kid ${Math.random()}`, purposeId: generateId(), }); - const tokenStateEntryPK2 = + const tokenGenStatesEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: generateId(), kid: `kid ${Math.random()}`, @@ -552,22 +582,28 @@ describe("V1 events", async () => { eserviceId: eservice.id, descriptorId: suspendedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -578,15 +614,15 @@ describe("V1 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -678,37 +714,45 @@ describe("V1 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: archivedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: archivedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: archivedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -716,27 +760,27 @@ describe("V1 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts index 5f94de02ee..93ce8f9ca2 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts @@ -20,7 +20,7 @@ import { EServiceDescriptorSuspendedV2, EServiceEventEnvelope, PlatformStatesCatalogEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, descriptorState, generateId, itemState, @@ -34,11 +34,11 @@ import { getMockDescriptor, getMockEService, getMockDocument, - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, buildDynamoDBTables, deleteDynamoDBTables, - readTokenStateEntriesByEserviceIdAndDescriptorId, - writeTokenStateEntry, + readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; @@ -107,37 +107,45 @@ describe("integration tests V2 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -150,17 +158,17 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -205,37 +213,45 @@ describe("integration tests V2 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -253,31 +269,29 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toEqual(expectedCatalogEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - // TODO: this works, but arrayContaining must have the exact objects - // expect.arrayContaining([expectedTokenStateEntry2, expectedTokenStateEntry2]) also passes the test - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -313,37 +327,45 @@ describe("integration tests V2 events", async () => { }); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); expect( handleMessageV2(message, dynamoDBClient) @@ -357,17 +379,17 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -419,33 +441,41 @@ describe("integration tests V2 events", async () => { eserviceId: eservice.id, descriptorId: archivedDescriptor.id, }); - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: archivedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: archivedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -453,27 +483,27 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -508,38 +538,46 @@ describe("integration tests V2 events", async () => { log_date: new Date(), }; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -562,27 +600,27 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -628,38 +666,46 @@ describe("integration tests V2 events", async () => { }; await writeCatalogEntry(previousStateEntry, dynamoDBClient); - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); const retrievedEntry = await readCatalogEntry( @@ -669,16 +715,16 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -725,58 +771,66 @@ describe("integration tests V2 events", async () => { eserviceId: eservice.id, descriptorId: publishedDescriptor.id, }); - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -825,33 +879,41 @@ describe("integration tests V2 events", async () => { eserviceId: eservice.id, descriptorId: archivedDescriptor.id, }); - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: publishedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -866,27 +928,27 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -936,37 +998,45 @@ describe("integration tests V2 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: suspendedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -975,17 +1045,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1031,37 +1101,45 @@ describe("integration tests V2 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: suspendedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1075,29 +1153,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1134,37 +1212,45 @@ describe("integration tests V2 events", async () => { }); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: suspendedDescriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: suspendedDescriptor.audience, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); expect( handleMessageV2(message, dynamoDBClient) @@ -1178,17 +1264,17 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -1248,39 +1334,47 @@ describe("integration tests V2 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: descriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: descriptor.audience, descriptorVoucherLifespan: descriptor.voucherLifespan, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: descriptor.audience, descriptorVoucherLifespan: descriptor.voucherLifespan, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1289,17 +1383,17 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry2, - previousTokenStateEntry1, + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, ]) ); }); @@ -1356,39 +1450,47 @@ describe("integration tests V2 events", async () => { await writeCatalogEntry(previousStateEntry, dynamoDBClient); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: descriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: descriptor.audience, descriptorVoucherLifespan: descriptor.voucherLifespan, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: descriptor.audience, descriptorVoucherLifespan: descriptor.voucherLifespan, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1402,29 +1504,29 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorVoucherLifespan: updatedDescriptor.voucherLifespan, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); @@ -1472,39 +1574,47 @@ describe("integration tests V2 events", async () => { }); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: descriptor.id, }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.active, descriptorAudience: descriptor.audience, descriptorVoucherLifespan: descriptor.voucherLifespan, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.active, descriptorAudience: descriptor.audience, descriptorVoucherLifespan: descriptor.voucherLifespan, GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); expect( handleMessageV2(message, dynamoDBClient) @@ -1518,17 +1628,17 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index 2caf1da813..e4c8d03cd8 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -13,7 +13,7 @@ import { } from "vitest"; import { PlatformStatesCatalogEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, descriptorState, generateId, itemState, @@ -26,12 +26,12 @@ import { DynamoDBClient, } from "@aws-sdk/client-dynamodb"; import { - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, buildDynamoDBTables, deleteDynamoDBTables, - readTokenStateEntriesByEserviceIdAndDescriptorId, - readAllTokenStateItems, - writeTokenStateEntry, + readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId, + readAllTokenGenStatesItems, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { deleteCatalogEntry, @@ -250,114 +250,145 @@ describe("utils tests", async () => { }); // token-generation-states - describe("writeTokenStateEntry", async () => { + describe("writeTokenGenStatesEntry", async () => { it("should throw error if previous entry exists", async () => { - const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + } + ); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: generateId(), descriptorId: generateId(), }); - const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, - }; - await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); expect( - writeTokenStateEntry(tokenStateEntry, dynamoDBClient) + writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ) ).rejects.toThrowError(ConditionalCheckFailedException); }); it("should write if previous entry doesn't exist", async () => { - const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + } + ); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: generateId(), descriptorId: generateId(), }); - const previousTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const previousTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(previousTokenStateEntries).toEqual([]); - const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, - }; - await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + expect(previousTokenGenStatesEntries).toEqual([]); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(retrievedTokenStateEntries).toEqual([tokenStateEntry]); + expect(retrievedTokenGenStatesEntries).toEqual([ + tokenGenStatesConsumerClient, + ]); }); }); - describe("readTokenStateEntriesByEserviceIdAndDescriptorId", async () => { + describe("readTokenGenStatesEntriesByEserviceIdAndDescriptorId", async () => { it("should return empty array if entries do not exist", async () => { const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: generateId(), descriptorId: generateId(), }); - const result = await readTokenStateEntriesByEserviceIdAndDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const result = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( + eserviceId_descriptorId, + dynamoDBClient + ); expect(result).toEqual([]); }); it("should return entries if they exist (no need for pagination)", async () => { - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: generateId(), descriptorId: generateId(), }); - const tokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, - }; - await writeTokenStateEntry(tokenStateEntry1, dynamoDBClient); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, - }; - await writeTokenStateEntry(tokenStateEntry2, dynamoDBClient); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); - const tokenEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const tokenGenStatesConsumerClients = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - expect(tokenEntries).toEqual( - expect.arrayContaining([tokenStateEntry1, tokenStateEntry2]) + expect(tokenGenStatesConsumerClients).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, + ]) ); }); @@ -369,35 +400,43 @@ describe("utils tests", async () => { const tokenEntriesLength = 10; - const writtenEntries: TokenGenerationStatesClientPurposeEntry[] = []; + const writtenTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = + []; // eslint-disable-next-line functional/no-let for (let i = 0; i < tokenEntriesLength; i++) { - const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), - descriptorState: itemState.inactive, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, - publicKey: crypto.randomBytes(100000).toString("hex"), - }; - await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + const tokenGenStatesEntryPK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + descriptorState: itemState.inactive, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); // eslint-disable-next-line functional/immutable-data - writtenEntries.push(tokenStateEntry); + writtenTokenGenStatesConsumerClients.push(tokenGenStatesConsumerClient); } vi.spyOn(dynamoDBClient, "send"); - const tokenEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const tokenGenStatesConsumerClients = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); - expect(tokenEntries).toHaveLength(tokenEntriesLength); - expect(tokenEntries).toEqual(expect.arrayContaining(writtenEntries)); + expect(tokenGenStatesConsumerClients).toHaveLength(tokenEntriesLength); + expect(tokenGenStatesConsumerClients).toEqual( + expect.arrayContaining(writtenTokenGenStatesConsumerClients) + ); }); }); @@ -407,8 +446,10 @@ describe("utils tests", async () => { eserviceId: generateId(), descriptorId: generateId(), }); - const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); - expect(tokenStateEntries).toEqual([]); + const tokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + expect(tokenGenStatesEntries).toEqual([]); expect( updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, @@ -416,72 +457,80 @@ describe("utils tests", async () => { dynamoDBClient ) ).resolves.not.toThrowError(); - const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(tokenStateEntriesAfterUpdate).toEqual([]); + expect(tokenGenStatesEntriesAfterUpdate).toEqual([]); }); it("should update state if previous entries exist", async () => { - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: generateId(), descriptorId: generateId(), }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), descriptorState: itemState.inactive, descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), descriptorState: itemState.inactive, descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], GSIPK_eserviceId_descriptorId: eserviceId_descriptorId, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, itemState.active, dynamoDBClient ); - const retrievedTokenStateEntries = - await readTokenStateEntriesByEserviceIdAndDescriptorId( + const retrievedTokenGenStatesEntries = + await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( eserviceId_descriptorId, dynamoDBClient ); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, descriptorState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry2, - expectedTokenStateEntry1, + expectedTokenGenStatesConsumeClient2, + expectedTokenGenStatesConsumeClient1, ]) ); }); diff --git a/packages/client-assertion-validation/src/errors.ts b/packages/client-assertion-validation/src/errors.ts index 1f1617a1f7..c9299d7797 100644 --- a/packages/client-assertion-validation/src/errors.ts +++ b/packages/client-assertion-validation/src/errors.ts @@ -1,4 +1,4 @@ -import { ApiError } from "pagopa-interop-models"; +import { ApiError, ItemState } from "pagopa-interop-models"; export const errorCodes = { unexpectedClientAssertionSignatureVerificationError: "0001", @@ -21,9 +21,9 @@ export const errorCodes = { tokenExpiredError: "0018", jsonWebTokenError: "0019", notBeforeError: "0020", - inactivePurpose: "0021", - inactiveAgreement: "0022", - inactiveEService: "0023", + invalidPurposeState: "0021", + invalidAgreementState: "0022", + invalidEServiceState: "0023", invalidClientIdFormat: "0024", invalidSubjectFormat: "0025", digestClaimNotFound: "0026", @@ -35,7 +35,6 @@ export const errorCodes = { invalidKidFormat: "0032", clientAssertionInvalidClaims: "0033", invalidSignature: "0034", - missingPlatformStates: "0035", }; export type ErrorCodes = keyof typeof errorCodes; @@ -213,26 +212,32 @@ export function notBeforeError(): ApiError { }); } -export function inactivePurpose(): ApiError { +export function invalidPurposeState( + purposeState: ItemState | undefined +): ApiError { return new ApiError({ - detail: "Purpose is not active", - code: "inactivePurpose", + detail: `Purpose state is: ${purposeState}`, + code: "invalidPurposeState", title: "Purpose is not active", }); } -export function inactiveEService(): ApiError { +export function invalidEServiceState( + eserviceState: ItemState | undefined +): ApiError { return new ApiError({ - detail: "E-Service is not active", - code: "inactiveEService", + detail: `E-Service state is: ${eserviceState}`, + code: "invalidEServiceState", title: "E-Service is not active", }); } -export function inactiveAgreement(): ApiError { +export function invalidAgreementState( + agreementState: ItemState | undefined +): ApiError { return new ApiError({ - detail: "Agreement is not active", - code: "inactiveAgreement", + detail: `Agreement state is: ${agreementState}`, + code: "invalidAgreementState", title: "Agreement is not active", }); } @@ -326,11 +331,3 @@ export function invalidSignature(): ApiError { title: "Invalid signature", }); } - -export function missingPlatformStates(): ApiError { - return new ApiError({ - detail: "Platform states not available for the entry", - code: "missingPlatformStates", - title: "Missing platform states", - }); -} diff --git a/packages/client-assertion-validation/src/utils.ts b/packages/client-assertion-validation/src/utils.ts index 44f16d1692..bc4bc4ce24 100644 --- a/packages/client-assertion-validation/src/utils.ts +++ b/packages/client-assertion-validation/src/utils.ts @@ -3,7 +3,7 @@ import { ClientId, itemState, PurposeId, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, unsafeBrandId, ClientAssertionDigest, } from "pagopa-interop-models"; @@ -23,9 +23,6 @@ import { invalidSubject, invalidPurposeIdClaimFormat, kidNotFound, - inactiveAgreement, - inactiveEService, - inactivePurpose, invalidClientIdFormat, invalidSubjectFormat, algorithmNotFound, @@ -36,6 +33,9 @@ import { digestClaimNotFound, audienceNotFound, invalidAudienceFormat, + invalidAgreementState, + invalidEServiceState, + invalidPurposeState, } from "./errors.js"; import { config } from "./config.js"; @@ -190,16 +190,22 @@ export const validateDigest = ( }; export const validatePlatformState = ( - key: TokenGenerationStatesClientPurposeEntry -): ValidationResult => { + key: TokenGenerationStatesConsumerClient +): ValidationResult => { const agreementError = - key.agreementState !== itemState.active ? inactiveAgreement() : undefined; + key.agreementState !== itemState.active + ? invalidAgreementState(key.agreementState) + : undefined; const descriptorError = - key.descriptorState !== itemState.active ? inactiveEService() : undefined; + key.descriptorState !== itemState.active + ? invalidEServiceState(key.descriptorState) + : undefined; const purposeError = - key.purposeState !== itemState.active ? inactivePurpose() : undefined; + key.purposeState !== itemState.active + ? invalidPurposeState(key.purposeState) + : undefined; if (!agreementError && !descriptorError && !purposeError) { return successfulValidation(key); diff --git a/packages/client-assertion-validation/src/validation.ts b/packages/client-assertion-validation/src/validation.ts index 27041566c5..8cde476757 100644 --- a/packages/client-assertion-validation/src/validation.ts +++ b/packages/client-assertion-validation/src/validation.ts @@ -1,12 +1,10 @@ import { match } from "ts-pattern"; import { - clientKidPurposePrefix, - clientKindTokenStates, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + clientKindTokenGenStates, ClientAssertion, ClientAssertionHeader, ClientAssertionPayload, + TokenGenerationStatesGenericClient, } from "pagopa-interop-models"; import * as jose from "jose"; import { @@ -55,7 +53,6 @@ import { clientAssertionInvalidClaims, algorithmNotAllowed, clientAssertionSignatureVerificationError, - missingPlatformStates, } from "./errors.js"; export const validateRequestParameters = ( @@ -187,9 +184,7 @@ export const verifyClientAssertion = ( export const verifyClientAssertionSignature = async ( clientAssertionJws: string, - key: - | TokenGenerationStatesClientPurposeEntry - | TokenGenerationStatesClientEntry, + key: TokenGenerationStatesGenericClient, clientAssertionAlgorithm: string ): Promise> => { try { @@ -247,28 +242,23 @@ export const verifyClientAssertionSignature = async ( }; export const validateClientKindAndPlatformState = ( - key: - | TokenGenerationStatesClientEntry - | TokenGenerationStatesClientPurposeEntry, + key: TokenGenerationStatesGenericClient, jwt: ClientAssertion ): ValidationResult => match(key) - .with({ clientKind: clientKindTokenStates.api }, () => + .with({ clientKind: clientKindTokenGenStates.api }, () => successfulValidation(jwt) ) - .with({ clientKind: clientKindTokenStates.consumer }, (key) => { - if (key.PK.startsWith(clientKidPurposePrefix)) { - const parsed = key as TokenGenerationStatesClientPurposeEntry; - const { errors: platformStateErrors } = validatePlatformState(parsed); - const purposeIdError = jwt.payload.purposeId - ? undefined - : purposeIdNotProvided(); + .with({ clientKind: clientKindTokenGenStates.consumer }, (key) => { + const { errors: platformStateErrors } = validatePlatformState(key); + const purposeIdError = jwt.payload.purposeId + ? undefined + : purposeIdNotProvided(); - if (!platformStateErrors && !purposeIdError) { - return successfulValidation(jwt); - } - return failedValidation([platformStateErrors, purposeIdError]); + if (!platformStateErrors && !purposeIdError) { + return successfulValidation(jwt); } - return failedValidation([missingPlatformStates()]); + + return failedValidation([platformStateErrors, purposeIdError]); }) .exhaustive(); diff --git a/packages/client-assertion-validation/test/utils.test.ts b/packages/client-assertion-validation/test/utils.test.ts index 522fc73f99..8bb21c2907 100644 --- a/packages/client-assertion-validation/test/utils.test.ts +++ b/packages/client-assertion-validation/test/utils.test.ts @@ -1,49 +1,79 @@ import { describe, expect, it } from "vitest"; -import { inactiveAgreement, inactiveEService } from "../src/errors.js"; +import { itemState } from "pagopa-interop-models"; +import { invalidAgreementState, invalidEServiceState } from "../src/errors.js"; import { failedValidation, successfulValidation } from "../src/utils.js"; describe("failedValidation", () => { it("array of errors", () => { - const errors = [inactiveEService(), inactiveAgreement()]; + const errors = [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + ]; const result = failedValidation(errors); expect(result).toEqual({ data: undefined, - errors: [inactiveEService(), inactiveAgreement()], + errors: [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + ], }); }); it("array of one error", () => { - const errors = [inactiveEService()]; + const errors = [invalidEServiceState(itemState.inactive)]; const result = failedValidation(errors); expect(result).toEqual({ data: undefined, - errors: [inactiveEService()], + errors: [invalidEServiceState(itemState.inactive)], }); }); it("array of errors or undefined", () => { - const errors = [inactiveEService(), inactiveAgreement(), undefined]; + const errors = [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + undefined, + ]; const result = failedValidation(errors); expect(result).toEqual({ data: undefined, - errors: [inactiveEService(), inactiveAgreement()], + errors: [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + ], }); }); it("nested array of errors", () => { - const errors = [[inactiveEService(), inactiveAgreement()], undefined]; + const errors = [ + [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + ], + undefined, + ]; const result = failedValidation(errors); expect(result).toEqual({ data: undefined, - errors: [inactiveEService(), inactiveAgreement()], + errors: [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + ], }); }); it("nested array of errors or undefined", () => { const errors = [ - [inactiveEService(), inactiveAgreement(), undefined], + [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + undefined, + ], undefined, ]; const result = failedValidation(errors); expect(result).toEqual({ data: undefined, - errors: [inactiveEService(), inactiveAgreement()], + errors: [ + invalidEServiceState(itemState.inactive), + invalidAgreementState(undefined), + ], }); }); }); diff --git a/packages/client-assertion-validation/test/validation.test.ts b/packages/client-assertion-validation/test/validation.test.ts index 22d1ced539..bdf1dcbdb7 100644 --- a/packages/client-assertion-validation/test/validation.test.ts +++ b/packages/client-assertion-validation/test/validation.test.ts @@ -3,19 +3,19 @@ import { fail } from "assert"; import { describe, expect, it } from "vitest"; import { ClientId, - clientKindTokenStates, + clientKindTokenGenStates, generateId, itemState, PurposeId, - TokenGenerationStatesClientEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import * as jsonwebtoken from "jsonwebtoken"; import { generateKeySet, getMockClientAssertion, - getMockTokenStatesClientEntry, - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesApiClient, + getMockTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { dateToSeconds } from "pagopa-interop-commons"; import { @@ -30,9 +30,9 @@ import { algorithmNotFound, digestClaimNotFound, expNotFound, - inactiveAgreement, - inactiveEService, - inactivePurpose, + invalidEServiceState, + invalidAgreementState, + invalidPurposeState, invalidAudience, invalidClientAssertionFormat, invalidClientIdFormat, @@ -56,7 +56,6 @@ import { clientAssertionInvalidClaims, invalidAudienceFormat, unexpectedClientAssertionSignatureVerificationError, - missingPlatformStates, } from "../src/errors.js"; import { ClientAssertionValidationRequest } from "../src/types.js"; import { getMockAccessTokenRequest, value64chars } from "./utils.js"; @@ -491,8 +490,8 @@ describe("validation test", async () => { exp: dateToSeconds(threeHourLater), }, }); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; const { errors } = await verifyClientAssertionSignature( @@ -506,8 +505,8 @@ describe("validation test", async () => { it("unexpectedClientAssertionSignatureVerificationError - base64 key expected", async () => { const { jws, publicKeyEncodedPem } = await getMockClientAssertion(); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: Buffer.from(publicKeyEncodedPem, "base64").toString("utf8"), }; @@ -539,8 +538,8 @@ describe("validation test", async () => { exp: dateToSeconds(threeHourLater), }, }); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; @@ -568,8 +567,8 @@ describe("validation test", async () => { }, }); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; const { errors } = await verifyClientAssertionSignature( @@ -583,8 +582,8 @@ describe("validation test", async () => { }); it("jsonWebTokenError", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; @@ -600,8 +599,8 @@ describe("validation test", async () => { it("invalidSignature", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; @@ -620,8 +619,8 @@ describe("validation test", async () => { }); it("jsonWebTokenError - malformed jwt", async () => { const { publicKeyEncodedPem } = generateKeySet(); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; @@ -639,8 +638,8 @@ describe("validation test", async () => { const { jws: clientAssertion1, publicKeyEncodedPem } = await getMockClientAssertion(); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; @@ -676,8 +675,8 @@ describe("validation test", async () => { nbf: dateToSeconds(threeHoursLater), }, }); - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), publicKey: publicKeyEncodedPem, }; @@ -698,30 +697,30 @@ describe("validation test", async () => { describe("validatePlatformState", async () => { it("success", async () => { - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - }; + const mockKey = getMockTokenGenStatesConsumerClient(); validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); expect(errors).toBeUndefined(); }); - it("inactiveAgreement", async () => { - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - agreementState: itemState.inactive, + it("invalidAgreementState", async () => { + const agreementState = itemState.inactive; + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + agreementState, }; validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(inactiveAgreement()); + expect(errors![0]).toEqual(invalidAgreementState(agreementState)); }); - it("inactiveEservice", async () => { - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - descriptorState: itemState.inactive, + it("invalidEServiceState", async () => { + const descriptorState = itemState.inactive; + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + descriptorState, descriptorAudience: ["test.interop.pagopa.it"], descriptorVoucherLifespan: 60, }; @@ -730,12 +729,13 @@ describe("validation test", async () => { expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(inactiveEService()); + expect(errors![0]).toEqual(invalidEServiceState(descriptorState)); }); - it("inactivePurpose", async () => { - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - purposeState: itemState.inactive, + it("invalidPurposeState", async () => { + const purposeState = itemState.inactive; + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + purposeState, }; validatePlatformState(mockKey); @@ -743,14 +743,17 @@ describe("validation test", async () => { expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(inactivePurpose()); + expect(errors![0]).toEqual(invalidPurposeState(purposeState)); }); - it("inactiveAgreement and inactiveEservice and inactivePurpose", async () => { - const mockKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - agreementState: itemState.inactive, - descriptorState: itemState.inactive, - purposeState: itemState.inactive, + it("invalidAgreementState and invalidEServiceState and invalidPurposeState", async () => { + const agreementState = itemState.inactive; + const descriptorState = itemState.inactive; + const purposeState = itemState.inactive; + const mockKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + agreementState, + descriptorState, + purposeState, }; validatePlatformState(mockKey); const { errors } = validatePlatformState(mockKey); @@ -758,16 +761,16 @@ describe("validation test", async () => { expect(errors).toBeDefined(); expect(errors).toHaveLength(3); expect(errors).toEqual([ - inactiveAgreement(), - inactiveEService(), - inactivePurpose(), + invalidAgreementState(agreementState), + invalidEServiceState(descriptorState), + invalidPurposeState(purposeState), ]); }); }); describe("validateClientKindAndPlatformState", async () => { it("success (clientKidPurpose entry with consumer client kind; valid platform states)", async () => { - const mockConsumerKey = getMockTokenStatesClientPurposeEntry(); + const mockConsumerKey = getMockTokenGenStatesConsumerClient(); const { data: mockClientAssertion } = verifyClientAssertion( ( await getMockClientAssertion({ @@ -786,10 +789,11 @@ describe("validation test", async () => { expect(errors).toBeUndefined(); }); - it("inactiveEService (consumerKey with consumer client kind; invalid platform states)", async () => { - const mockConsumerKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - descriptorState: itemState.inactive, + it("invalidEServiceState (consumerKey with consumer client kind; invalid platform states)", async () => { + const descriptorState = itemState.inactive; + const mockConsumerKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + descriptorState, }; const { data: mockClientAssertion } = verifyClientAssertion( ( @@ -808,13 +812,13 @@ describe("validation test", async () => { ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(inactiveEService()); + expect(errors![0]).toEqual(invalidEServiceState(descriptorState)); }); it("success (clientEntry with api client kind)", async () => { - const mockApiKey: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), - clientKind: clientKindTokenStates.api, + const mockApiKey: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + clientKind: clientKindTokenGenStates.api, }; const { data: mockClientAssertion } = verifyClientAssertion( (await getMockClientAssertion()).jws, @@ -830,29 +834,38 @@ describe("validation test", async () => { expect(errors).toBeUndefined(); }); - it("missingPlatformStates (clientEntry with consumer client kind)", async () => { - const mockApiKey: TokenGenerationStatesClientEntry = { - ...getMockTokenStatesClientEntry(), - clientKind: clientKindTokenStates.consumer, + it("invalidPurposeState for consumer client", async () => { + const mockConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + clientKind: clientKindTokenGenStates.consumer, + agreementState: itemState.active, + descriptorState: itemState.active, + purposeState: undefined, }; const { data: mockClientAssertion } = verifyClientAssertion( - (await getMockClientAssertion()).jws, + ( + await getMockClientAssertion({ + customClaims: { purposeId: mockConsumerClient.GSIPK_purposeId }, + }) + ).jws, undefined ); if (!mockClientAssertion) { fail(); } const { errors } = validateClientKindAndPlatformState( - mockApiKey, + mockConsumerClient, mockClientAssertion ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(missingPlatformStates()); + expect(errors).toEqual([ + invalidPurposeState(mockConsumerClient.purposeState), + ]); }); it("purposeIdNotProvided for Client Kind Consumer", async () => { - const mockConsumerKey = getMockTokenStatesClientPurposeEntry(); + const mockConsumerKey = getMockTokenGenStatesConsumerClient(); const { data: mockClientAssertion } = verifyClientAssertion( ( await getMockClientAssertion({ @@ -874,9 +887,10 @@ describe("validation test", async () => { }); it("purposeIdNotProvided and platformStateError", async () => { - const mockConsumerKey: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(), - agreementState: itemState.inactive, + const agreementState = itemState.inactive; + const mockConsumerKey: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + agreementState, }; const { data: mockClientAssertion } = verifyClientAssertion( ( @@ -895,7 +909,10 @@ describe("validation test", async () => { ); expect(errors).toBeDefined(); expect(errors).toHaveLength(2); - expect(errors).toEqual([inactiveAgreement(), purposeIdNotProvided()]); + expect(errors).toEqual([ + invalidAgreementState(agreementState), + purposeIdNotProvided(), + ]); }); }); }); diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 80c01f9ec7..706be8db76 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -40,13 +40,13 @@ import { itemState, ClientId, PurposeId, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, makeGSIPKConsumerIdEServiceId, makeGSIPKClientIdPurposeId, makeGSIPKEServiceIdDescriptorId, TokenGenerationStatesClientKidPurposePK, makeTokenGenerationStatesClientKidPurposePK, - clientKindTokenStates, + clientKindTokenGenStates, AgreementId, PurposeVersionId, ProducerKeychain, @@ -63,7 +63,7 @@ import { PlatformStatesAgreementPK, makeGSIPKKid, TokenGenerationStatesClientKidPK, - TokenGenerationStatesClientEntry, + TokenGenerationStatesApiClient, makeTokenGenerationStatesClientKidPK, PlatformStatesClientPK, PlatformStatesClientEntry, @@ -439,11 +439,13 @@ export const getMockDelegationDocument = ( createdAt: new Date(), }); -export const getMockTokenStatesClientPurposeEntry = ( - tokenStateEntryPK?: TokenGenerationStatesClientKidPurposePK -): TokenGenerationStatesClientPurposeEntry => { - const clientId = tokenStateEntryPK - ? unsafeBrandId(tokenStateEntryPK.split("#")[1]) +export const getMockTokenGenStatesConsumerClient = ( + tokenGenStatesEntryPK?: + | TokenGenerationStatesClientKidPurposePK + | TokenGenerationStatesClientKidPK +): TokenGenerationStatesConsumerClient => { + const clientId = tokenGenStatesEntryPK + ? unsafeBrandId(tokenGenStatesEntryPK.split("#")[1]) : generateId(); const purposeId = generateId(); const consumerId = generateId(); @@ -453,44 +455,60 @@ export const getMockTokenStatesClientPurposeEntry = ( const purposeVersionId = generateId(); const kid = `kid ${Math.random()}`; - return { - PK: - tokenStateEntryPK || - makeTokenGenerationStatesClientKidPurposePK({ + if ( + !tokenGenStatesEntryPK || + TokenGenerationStatesClientKidPurposePK.safeParse(tokenGenStatesEntryPK) + .success + ) { + return { + PK: + tokenGenStatesEntryPK || + makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }), + descriptorState: itemState.active, + descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], + descriptorVoucherLifespan: 60, + updatedAt: new Date().toISOString(), + consumerId, + agreementId, + purposeVersionId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: "PEM", + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + agreementState: itemState.active, + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId, + }), + GSIPK_purposeId: purposeId, + purposeState: itemState.active, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, - kid, purposeId, }), - descriptorState: itemState.active, - descriptorAudience: ["pagopa.it/test1", "pagopa.it/test2"], - descriptorVoucherLifespan: 60, - updatedAt: new Date().toISOString(), - consumerId, - agreementId, - purposeVersionId, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + }; + } else { + return { + PK: tokenGenStatesEntryPK, + updatedAt: new Date().toISOString(), consumerId, - eserviceId, - }), - clientKind: clientKindTokenStates.consumer, - publicKey: "PEM", - GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), - agreementState: itemState.active, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId, - descriptorId, - }), - GSIPK_purposeId: purposeId, - purposeState: itemState.active, - GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ - clientId, - purposeId, - }), - }; + clientKind: clientKindTokenGenStates.consumer, + publicKey: "PEM", + GSIPK_clientId: clientId, + GSIPK_kid: makeGSIPKKid(kid), + }; + } }; -export const getMockAgreementEntry = ( +export const getMockPlatformStatesAgreementEntry = ( primaryKey: PlatformStatesAgreementPK, GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId( { @@ -508,11 +526,11 @@ export const getMockAgreementEntry = ( agreementDescriptorId: generateId(), }); -export const getMockTokenStatesClientEntry = ( - tokenStateEntryPK?: TokenGenerationStatesClientKidPK -): TokenGenerationStatesClientEntry => { - const clientId = tokenStateEntryPK - ? unsafeBrandId(tokenStateEntryPK.split("#")[1]) +export const getMockTokenGenStatesApiClient = ( + tokenGenStatesEntryPK?: TokenGenerationStatesClientKidPK +): TokenGenerationStatesApiClient => { + const clientId = tokenGenStatesEntryPK + ? unsafeBrandId(tokenGenStatesEntryPK.split("#")[1]) : generateId(); const consumerId = generateId(); @@ -520,14 +538,14 @@ export const getMockTokenStatesClientEntry = ( return { PK: - tokenStateEntryPK || + tokenGenStatesEntryPK || makeTokenGenerationStatesClientKidPK({ clientId, kid, }), updatedAt: new Date().toISOString(), consumerId, - clientKind: clientKindTokenStates.consumer, + clientKind: clientKindTokenGenStates.api, publicKey: "PEM", GSIPK_clientId: clientId, GSIPK_kid: makeGSIPKKid(kid), diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index ca231469ad..900d1d7705 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -16,42 +16,42 @@ import { GSIPKEServiceIdDescriptorId, PlatformStatesCatalogEntry, PlatformStatesGenericEntry, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, PlatformStatesPurposeEntry, PlatformStatesAgreementEntry, - TokenGenerationStatesGenericEntry, - TokenGenerationStatesClientEntry, + TokenGenerationStatesGenericClient, + TokenGenerationStatesApiClient, } from "pagopa-interop-models"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; -export const writeTokenStateClientEntry = async ( - tokenStateEntry: TokenGenerationStatesClientEntry, +export const writeTokenGenStatesApiClient = async ( + tokenGenStatesEntry: TokenGenerationStatesApiClient, dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", Item: { PK: { - S: tokenStateEntry.PK, + S: tokenGenStatesEntry.PK, }, updatedAt: { - S: tokenStateEntry.updatedAt, + S: tokenGenStatesEntry.updatedAt, }, consumerId: { - S: tokenStateEntry.consumerId, + S: tokenGenStatesEntry.consumerId, }, clientKind: { - S: tokenStateEntry.clientKind, + S: tokenGenStatesEntry.clientKind, }, publicKey: { - S: tokenStateEntry.publicKey, + S: tokenGenStatesEntry.publicKey, }, GSIPK_clientId: { - S: tokenStateEntry.GSIPK_clientId, + S: tokenGenStatesEntry.GSIPK_clientId, }, GSIPK_kid: { - S: tokenStateEntry.GSIPK_kid, + S: tokenGenStatesEntry.GSIPK_kid, }, }, TableName: "token-generation-states", @@ -60,110 +60,110 @@ export const writeTokenStateClientEntry = async ( await dynamoDBClient.send(command); }; -export const writeTokenStateEntry = async ( - tokenStateEntry: TokenGenerationStatesClientPurposeEntry, +export const writeTokenGenStatesConsumerClient = async ( + tokenGenStatesEntry: TokenGenerationStatesConsumerClient, dynamoDBClient: DynamoDBClient ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", Item: { PK: { - S: tokenStateEntry.PK, + S: tokenGenStatesEntry.PK, }, - ...(tokenStateEntry.descriptorState + ...(tokenGenStatesEntry.descriptorState ? { descriptorState: { - S: tokenStateEntry.descriptorState, + S: tokenGenStatesEntry.descriptorState, }, } : {}), - ...(tokenStateEntry.descriptorAudience + ...(tokenGenStatesEntry.descriptorAudience ? { descriptorAudience: { - L: tokenStateEntry.descriptorAudience.map((item) => ({ + L: tokenGenStatesEntry.descriptorAudience.map((item) => ({ S: item, })), }, } : {}), - ...(tokenStateEntry.descriptorVoucherLifespan + ...(tokenGenStatesEntry.descriptorVoucherLifespan ? { descriptorVoucherLifespan: { - N: tokenStateEntry.descriptorVoucherLifespan.toString(), + N: tokenGenStatesEntry.descriptorVoucherLifespan.toString(), }, } : {}), updatedAt: { - S: tokenStateEntry.updatedAt, + S: tokenGenStatesEntry.updatedAt, }, consumerId: { - S: tokenStateEntry.consumerId, + S: tokenGenStatesEntry.consumerId, }, - ...(tokenStateEntry.agreementId + ...(tokenGenStatesEntry.agreementId ? { agreementId: { - S: tokenStateEntry.agreementId, + S: tokenGenStatesEntry.agreementId, }, } : {}), - ...(tokenStateEntry.purposeVersionId + ...(tokenGenStatesEntry.purposeVersionId ? { purposeVersionId: { - S: tokenStateEntry.purposeVersionId, + S: tokenGenStatesEntry.purposeVersionId, }, } : {}), - ...(tokenStateEntry.GSIPK_consumerId_eserviceId + ...(tokenGenStatesEntry.GSIPK_consumerId_eserviceId ? { GSIPK_consumerId_eserviceId: { - S: tokenStateEntry.GSIPK_consumerId_eserviceId, + S: tokenGenStatesEntry.GSIPK_consumerId_eserviceId, }, } : {}), clientKind: { - S: tokenStateEntry.clientKind, + S: tokenGenStatesEntry.clientKind, }, publicKey: { - S: tokenStateEntry.publicKey, + S: tokenGenStatesEntry.publicKey, }, GSIPK_clientId: { - S: tokenStateEntry.GSIPK_clientId, + S: tokenGenStatesEntry.GSIPK_clientId, }, GSIPK_kid: { - S: tokenStateEntry.GSIPK_kid, + S: tokenGenStatesEntry.GSIPK_kid, }, - ...(tokenStateEntry.GSIPK_clientId_purposeId + ...(tokenGenStatesEntry.GSIPK_clientId_purposeId ? { GSIPK_clientId_purposeId: { - S: tokenStateEntry.GSIPK_clientId_purposeId, + S: tokenGenStatesEntry.GSIPK_clientId_purposeId, }, } : {}), - ...(tokenStateEntry.agreementState + ...(tokenGenStatesEntry.agreementState ? { agreementState: { - S: tokenStateEntry.agreementState, + S: tokenGenStatesEntry.agreementState, }, } : {}), - ...(tokenStateEntry.GSIPK_eserviceId_descriptorId + ...(tokenGenStatesEntry.GSIPK_eserviceId_descriptorId ? { GSIPK_eserviceId_descriptorId: { - S: tokenStateEntry.GSIPK_eserviceId_descriptorId, + S: tokenGenStatesEntry.GSIPK_eserviceId_descriptorId, }, } : {}), - ...(tokenStateEntry.GSIPK_purposeId + ...(tokenGenStatesEntry.GSIPK_purposeId ? { GSIPK_purposeId: { - S: tokenStateEntry.GSIPK_purposeId, + S: tokenGenStatesEntry.GSIPK_purposeId, }, } : {}), - ...(tokenStateEntry.purposeState + ...(tokenGenStatesEntry.purposeState ? { purposeState: { - S: tokenStateEntry.purposeState, + S: tokenGenStatesEntry.purposeState, }, } : {}), @@ -174,9 +174,9 @@ export const writeTokenStateEntry = async ( await dynamoDBClient.send(command); }; -export const readAllTokenStateItems = async ( +export const readAllTokenGenStatesItems = async ( dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const readInput: ScanInput = { TableName: "token-generation-states", }; @@ -190,22 +190,22 @@ export const readAllTokenStateItems = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesGenericEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesGenericClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } }; -export const readAllPlatformStateItems = async ( +export const readAllPlatformStatesItems = async ( dynamoDBClient: DynamoDBClient ): Promise => { const readInput: ScanInput = { @@ -236,21 +236,21 @@ export const readAllPlatformStateItems = async ( } }; -export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( - eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, +export const readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId = async ( + gsiPKEServiceIdDescriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( - eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, + gsiPKEServiceIdDescriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: "token-generation-states", IndexName: "Descriptor", KeyConditionExpression: `GSIPK_eserviceId_descriptorId = :gsiValue`, ExpressionAttributeValues: { - ":gsiValue": { S: eserviceId_descriptorId }, + ":gsiValue": { S: gsiPKEServiceIdDescriptorId }, }, ExclusiveStartKey: exclusiveStartKey, }; @@ -264,25 +264,25 @@ export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } if (!data.LastEvaluatedKey) { - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } else { return [ - ...tokenStateEntries.data, + ...tokenGenStatesEntries.data, ...(await runPaginatedQuery( - eserviceId_descriptorId, + gsiPKEServiceIdDescriptorId, dynamoDBClient, data.LastEvaluatedKey )), @@ -292,27 +292,27 @@ export const readTokenStateEntriesByEserviceIdAndDescriptorId = async ( }; return await runPaginatedQuery( - eserviceId_descriptorId, + gsiPKEServiceIdDescriptorId, dynamoDBClient, undefined ); }; -export const readTokenStateEntriesByConsumerIdEserviceId = async ( - consumerId_eserviceId: GSIPKConsumerIdEServiceId, +export const readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId = async ( + gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( - consumerId_eserviceId: GSIPKConsumerIdEServiceId, + gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: "token-generation-states", IndexName: "Agreement", KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, ExpressionAttributeValues: { - ":gsiValue": { S: consumerId_eserviceId }, + ":gsiValue": { S: gsiPKConsumerIdEServiceId }, }, ExclusiveStartKey: exclusiveStartKey, }; @@ -326,25 +326,25 @@ export const readTokenStateEntriesByConsumerIdEserviceId = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } if (!data.LastEvaluatedKey) { - return tokenStateEntries.data; + return tokenGenStatesEntries.data; } else { return [ - ...tokenStateEntries.data, + ...tokenGenStatesEntries.data, ...(await runPaginatedQuery( - consumerId_eserviceId, + gsiPKConsumerIdEServiceId, dynamoDBClient, data.LastEvaluatedKey )), @@ -354,13 +354,13 @@ export const readTokenStateEntriesByConsumerIdEserviceId = async ( }; return await runPaginatedQuery( - consumerId_eserviceId, + gsiPKConsumerIdEServiceId, dynamoDBClient, undefined ); }; -export const writeCatalogEntry = async ( +export const writePlatformCatalogEntry = async ( catalogEntry: PlatformStatesCatalogEntry, dynamoDBClient: DynamoDBClient ): Promise => { diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 201c9c74dd..3ea606295c 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -82,24 +82,37 @@ export const DelegationContractId = z .uuid() .brand("DelegationContractId"); export type DelegationContractId = z.infer; + +const eserviceDescriptorPrefix = "ESERVICEDESCRIPTOR#"; export const PlatformStatesEServiceDescriptorPK = z .string() - .brand(`ESERVICEDESCRIPTOR#eServiceId#descriptorId`); + .refine((pk) => pk.startsWith(eserviceDescriptorPrefix)) + .brand(`${eserviceDescriptorPrefix}eServiceId#descriptorId`); export type PlatformStatesEServiceDescriptorPK = z.infer< typeof PlatformStatesEServiceDescriptorPK >; +const agreementPrefix = "AGREEMENT#"; export const PlatformStatesAgreementPK = z .string() - .brand(`AGREEMENT#agreementId`); + .refine((pk) => pk.startsWith(agreementPrefix)) + .brand(`${agreementPrefix}agreementId`); export type PlatformStatesAgreementPK = z.infer< typeof PlatformStatesAgreementPK >; -export const PlatformStatesPurposePK = z.string().brand(`PURPOSE#purposeId`); +const purposePrefix = "PURPOSE#"; +export const PlatformStatesPurposePK = z + .string() + .refine((pk) => pk.startsWith(purposePrefix)) + .brand(`${purposePrefix}purposeId`); export type PlatformStatesPurposePK = z.infer; -export const PlatformStatesClientPK = z.string().brand(`CLIENT#clientId`); +const clientPrefix = "CLIENT#"; +export const PlatformStatesClientPK = z + .string() + .refine((pk) => pk.startsWith(clientPrefix)) + .brand(`${clientPrefix}clientId`); export type PlatformStatesClientPK = z.infer; export const GSIPKConsumerIdEServiceId = z diff --git a/packages/models/src/index.ts b/packages/models/src/index.ts index efba64d4d7..cdfc1df1a9 100644 --- a/packages/models/src/index.ts +++ b/packages/models/src/index.ts @@ -59,6 +59,7 @@ export * from "./authorization/authorizationReadModelAdapter.js"; export * from "./user/user.js"; +// Token generation read model export * from "./token-generation-readmodel/platform-states-entry.js"; export * from "./token-generation-readmodel/token-generation-states-entry.js"; export * from "./token-generation-readmodel/commons.js"; diff --git a/packages/models/src/token-generation-readmodel/commons.ts b/packages/models/src/token-generation-readmodel/commons.ts index ea1cc7ea38..5c064660ff 100644 --- a/packages/models/src/token-generation-readmodel/commons.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -99,12 +99,12 @@ export const makeGSIPKClientIdPurposeId = ({ export const makeGSIPKKid = (kid: string): GSIPKKid => unsafeBrandId(kid); -export const clientKindTokenStates = { +export const clientKindTokenGenStates = { consumer: "CONSUMER", api: "API", } as const; -export const ClientKindTokenStates = z.enum([ - Object.values(clientKindTokenStates)[0], - ...Object.values(clientKindTokenStates).slice(1), +export const ClientKindTokenGenStates = z.enum([ + Object.values(clientKindTokenGenStates)[0], + ...Object.values(clientKindTokenGenStates).slice(1), ]); -export type ClientKindTokenStates = z.infer; +export type ClientKindTokenGenStates = z.infer; diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index 4563e3b8db..29c769a459 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -11,7 +11,7 @@ import { PurposeVersionId, TenantId, } from "../brandedIds.js"; -import { ClientKindTokenStates } from "./commons.js"; +import { ClientKindTokenGenStates } from "./commons.js"; export const itemState = { active: "ACTIVE", @@ -61,7 +61,7 @@ export type PlatformStatesAgreementEntry = z.infer< export const PlatformStatesClientEntry = PlatformStatesBaseEntry.extend({ PK: PlatformStatesClientPK, - clientKind: ClientKindTokenStates, + clientKind: ClientKindTokenGenStates, clientConsumerId: TenantId, clientPurposesIds: z.array(PurposeId), }); diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index 1f82f59bf6..193146cfb1 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -13,11 +13,10 @@ import { TokenGenerationStatesClientKidPurposePK, } from "../brandedIds.js"; import { ItemState } from "./platform-states-entry.js"; -import { ClientKindTokenStates } from "./commons.js"; +import { clientKindTokenGenStates } from "./commons.js"; const TokenGenerationStatesBaseEntry = z.object({ consumerId: TenantId, - clientKind: ClientKindTokenStates, publicKey: z.string(), GSIPK_clientId: ClientId, GSIPK_kid: GSIPKKid, @@ -27,9 +26,12 @@ type TokenGenerationStatesBaseEntry = z.infer< typeof TokenGenerationStatesBaseEntry >; -export const TokenGenerationStatesClientPurposeEntry = +export const TokenGenerationStatesConsumerClient = TokenGenerationStatesBaseEntry.extend({ - PK: TokenGenerationStatesClientKidPurposePK, + PK: TokenGenerationStatesClientKidPurposePK.or( + TokenGenerationStatesClientKidPK + ), + clientKind: z.literal(clientKindTokenGenStates.consumer), GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId.optional(), agreementId: AgreementId.optional(), agreementState: ItemState.optional(), @@ -42,26 +44,29 @@ export const TokenGenerationStatesClientPurposeEntry = purposeVersionId: PurposeVersionId.optional(), GSIPK_clientId_purposeId: GSIPKClientIdPurposeId.optional(), }); -export type TokenGenerationStatesClientPurposeEntry = z.infer< - typeof TokenGenerationStatesClientPurposeEntry +export type TokenGenerationStatesConsumerClient = z.infer< + typeof TokenGenerationStatesConsumerClient >; -export const FullTokenGenerationStatesClientPurposeEntry = - TokenGenerationStatesClientPurposeEntry.required(); -export type FullTokenGenerationStatesClientPurposeEntry = z.infer< - typeof FullTokenGenerationStatesClientPurposeEntry +export const FullTokenGenerationStatesConsumerClient = + TokenGenerationStatesConsumerClient.required().extend({ + PK: TokenGenerationStatesClientKidPurposePK, + }); +export type FullTokenGenerationStatesConsumerClient = z.infer< + typeof FullTokenGenerationStatesConsumerClient >; -export const TokenGenerationStatesClientEntry = +export const TokenGenerationStatesApiClient = TokenGenerationStatesBaseEntry.extend({ PK: TokenGenerationStatesClientKidPK, + clientKind: z.literal(clientKindTokenGenStates.api), }); -export type TokenGenerationStatesClientEntry = z.infer< - typeof TokenGenerationStatesClientEntry +export type TokenGenerationStatesApiClient = z.infer< + typeof TokenGenerationStatesApiClient >; -export const TokenGenerationStatesGenericEntry = - TokenGenerationStatesClientPurposeEntry.or(TokenGenerationStatesClientEntry); -export type TokenGenerationStatesGenericEntry = z.infer< - typeof TokenGenerationStatesGenericEntry +export const TokenGenerationStatesGenericClient = + TokenGenerationStatesConsumerClient.or(TokenGenerationStatesApiClient); +export type TokenGenerationStatesGenericClient = z.infer< + typeof TokenGenerationStatesGenericClient >; diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV1.ts b/packages/purpose-platformstate-writer/src/consumerServiceV1.ts index 5b557f34b0..8e5b6e5b65 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV1.ts @@ -16,8 +16,8 @@ import { updatePurposeDataInPlatformStatesEntry, writePlatformPurposeEntry, getLastSuspendedOrActivatedPurposeVersion, - updatePurposeDataInTokenEntries, - updateTokenEntriesWithPurposeAndPlatformStatesData, + updatePurposeDataInTokenGenStatesEntries, + updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData, getLastArchivedPurposeVersion, } from "./utils.js"; @@ -53,7 +53,7 @@ export async function handleMessageV1( }); // token-generation-states - await updatePurposeDataInTokenEntries({ + await updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState, @@ -74,7 +74,7 @@ export async function handleMessageV1( await writePlatformPurposeEntry(dynamoDBClient, purposeEntry); // token-generation-states - await updateTokenEntriesWithPurposeAndPlatformStatesData( + await updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( dynamoDBClient, purpose, purposeState, @@ -105,7 +105,7 @@ export async function handleMessageV1( }); // token-generation-states - await updatePurposeDataInTokenEntries({ + await updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState, @@ -121,7 +121,7 @@ export async function handleMessageV1( await deletePlatformPurposeEntry(dynamoDBClient, primaryKey); // token-generation-states - await updatePurposeDataInTokenEntries({ + await updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState: getPurposeStateFromPurposeVersions(purpose.versions), diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts index 10c7c38794..c884b9b894 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts @@ -18,9 +18,9 @@ import { getPurposeStateFromPurposeVersions, readPlatformPurposeEntry, updatePurposeDataInPlatformStatesEntry, - updatePurposeDataInTokenEntries, + updatePurposeDataInTokenGenStatesEntries, writePlatformPurposeEntry, - updateTokenEntriesWithPurposeAndPlatformStatesData, + updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData, getLastSuspendedOrActivatedPurposeVersion, } from "./utils.js"; @@ -73,7 +73,7 @@ export async function handleMessageV2( } // token-generation-states - await updateTokenEntriesWithPurposeAndPlatformStatesData( + await updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( dynamoDBClient, purpose, purposeState, @@ -116,7 +116,7 @@ export async function handleMessageV2( }); // token-generation-states - await updatePurposeDataInTokenEntries({ + await updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState, @@ -136,7 +136,7 @@ export async function handleMessageV2( await deletePlatformPurposeEntry(dynamoDBClient, primaryKey); // token-generation-states - await updatePurposeDataInTokenEntries({ + await updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState: getPurposeStateFromPurposeVersions(purpose.versions), diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index de840e33e6..4deb704fde 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -35,7 +35,7 @@ import { PurposeVersion, PurposeVersionId, purposeVersionState, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { z } from "zod"; import { config } from "./config/config.js"; @@ -129,12 +129,12 @@ export const deletePlatformPurposeEntry = async ( await dynamoDBClient.send(command); }; -export const readTokenEntriesByGSIPKPurposeId = async ( +export const readTokenGenStatesEntriesByGSIPKPurposeId = async ( dynamoDBClient: DynamoDBClient, purposeId: PurposeId, exclusiveStartKey?: Record ): Promise<{ - tokenStateEntries: TokenGenerationStatesClientPurposeEntry[]; + tokenGenStatesEntries: TokenGenerationStatesConsumerClient[]; lastEvaluatedKey?: Record; }> => { const input: QueryInput = { @@ -156,20 +156,20 @@ export const readTokenEntriesByGSIPKPurposeId = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const tokenStateEntries = z - .array(TokenGenerationStatesClientPurposeEntry) + const tokenGenStatesEntries = z + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); - if (!tokenStateEntries.success) { + if (!tokenGenStatesEntries.success) { throw genericInternalError( `Unable to parse token state entry item: result ${JSON.stringify( - tokenStateEntries + tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } return { - tokenStateEntries: tokenStateEntries.data, + tokenGenStatesEntries: tokenGenStatesEntries.data, lastEvaluatedKey: data.LastEvaluatedKey, }; } @@ -221,53 +221,56 @@ export const updatePurposeDataInPlatformStatesEntry = async ({ await dynamoDBClient.send(command); }; -export const updateTokenEntriesWithPurposeAndPlatformStatesData = async ( - dynamoDBClient: DynamoDBClient, - purpose: Purpose, - purposeState: ItemState, - purposeVersionId: PurposeVersionId -): Promise => { - const runPaginatedUpdateQuery = async ( +export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = + async ( dynamoDBClient: DynamoDBClient, purpose: Purpose, purposeState: ItemState, - purposeVersionId: PurposeVersionId, - exclusiveStartKey?: Record + purposeVersionId: PurposeVersionId ): Promise => { - const result = await readTokenEntriesByGSIPKPurposeId( - dynamoDBClient, - purpose.id, - exclusiveStartKey - ); - const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ - consumerId: purpose.consumerId, - eserviceId: purpose.eserviceId, - }); - const platformAgreementEntry = await readPlatformAgreementEntry( - dynamoDBClient, - gsiPKConsumerIdEServiceId - ); - const catalogEntry = platformAgreementEntry - ? await readCatalogEntry( - dynamoDBClient, - makePlatformStatesEServiceDescriptorPK({ - eserviceId: purpose.eserviceId, - descriptorId: platformAgreementEntry.agreementDescriptorId, - }) - ) - : undefined; - - for (const entry of result.tokenStateEntries) { - const tokenEntryPK = entry.PK; - const isAgreementMissingInTokenTable = - platformAgreementEntry && - (!entry.agreementId || - !entry.agreementState || - !entry.GSIPK_eserviceId_descriptorId); - - // Agreement data from platform-states - const agreementExpressionAttributeValues: Record = - isAgreementMissingInTokenTable + const runPaginatedUpdateQuery = async ( + dynamoDBClient: DynamoDBClient, + purpose: Purpose, + purposeState: ItemState, + purposeVersionId: PurposeVersionId, + exclusiveStartKey?: Record + ): Promise => { + const result = await readTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purpose.id, + exclusiveStartKey + ); + const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); + const platformAgreementEntry = await readPlatformAgreementEntry( + dynamoDBClient, + gsiPKConsumerIdEServiceId + ); + const catalogEntry = platformAgreementEntry + ? await readCatalogEntry( + dynamoDBClient, + makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: platformAgreementEntry.agreementDescriptorId, + }) + ) + : undefined; + + for (const entry of result.tokenGenStatesEntries) { + const tokenEntryPK = entry.PK; + const isAgreementMissingInTokenGenStates = + platformAgreementEntry && + (!entry.agreementId || + !entry.agreementState || + !entry.GSIPK_eserviceId_descriptorId); + + // Agreement data from platform-states + const agreementExpressionAttributeValues: Record< + string, + AttributeValue + > = isAgreementMissingInTokenGenStates ? { ":agreementId": { S: extractAgreementIdFromAgreementPK(platformAgreementEntry.PK), @@ -283,101 +286,102 @@ export const updateTokenEntriesWithPurposeAndPlatformStatesData = async ( }, } : {}; - const agreementUpdateExpression = isAgreementMissingInTokenTable - ? `, agreementId = :agreementId, + const agreementUpdateExpression = isAgreementMissingInTokenGenStates + ? `, agreementId = :agreementId, agreementState = :agreementState, GSIPK_eserviceId_descriptorId = :GSIPK_eserviceId_descriptorId` - : ""; - - // Descriptor data from platform-states - const isDescriptorDataMissingInTokenTable = - platformAgreementEntry && - catalogEntry && - (!entry.descriptorAudience || - !entry.descriptorState || - !entry.descriptorVoucherLifespan); - - const descriptorExpressionAttributeValues: Record< - string, - AttributeValue - > = isDescriptorDataMissingInTokenTable - ? { - ":descriptorState": { - S: catalogEntry.state, - }, - ":descriptorAudience": { - L: catalogEntry.descriptorAudience.map((item) => ({ - S: item, - })), - }, - ":descriptorVoucherLifespan": { - N: catalogEntry.descriptorVoucherLifespan.toString(), - }, - } - : {}; - const descriptorUpdateExpression = isDescriptorDataMissingInTokenTable - ? `, descriptorState = :descriptorState, + : ""; + + // Descriptor data from platform-states + const isDescriptorDataMissingInTokenGenStates = + platformAgreementEntry && + catalogEntry && + (!entry.descriptorAudience || + !entry.descriptorState || + !entry.descriptorVoucherLifespan); + + const descriptorExpressionAttributeValues: Record< + string, + AttributeValue + > = isDescriptorDataMissingInTokenGenStates + ? { + ":descriptorState": { + S: catalogEntry.state, + }, + ":descriptorAudience": { + L: catalogEntry.descriptorAudience.map((item) => ({ + S: item, + })), + }, + ":descriptorVoucherLifespan": { + N: catalogEntry.descriptorVoucherLifespan.toString(), + }, + } + : {}; + const descriptorUpdateExpression = + isDescriptorDataMissingInTokenGenStates + ? `, descriptorState = :descriptorState, descriptorAudience = :descriptorAudience, descriptorVoucherLifespan = :descriptorVoucherLifespan` - : ""; + : ""; - const input: UpdateItemInput = { - ConditionExpression: "attribute_exists(PK)", - Key: { - PK: { - S: tokenEntryPK, - }, - }, - ExpressionAttributeValues: { - ...agreementExpressionAttributeValues, - ...descriptorExpressionAttributeValues, - ":newState": { - S: purposeState, - }, - ":newPurposeVersionId": { - S: purposeVersionId, - }, - ":GSIPK_consumerId_eserviceId": { - S: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose.consumerId, - eserviceId: purpose.eserviceId, - }), + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: tokenEntryPK, + }, }, - ":newUpdatedAt": { - S: new Date().toISOString(), + ExpressionAttributeValues: { + ...agreementExpressionAttributeValues, + ...descriptorExpressionAttributeValues, + ":newState": { + S: purposeState, + }, + ":newPurposeVersionId": { + S: purposeVersionId, + }, + ":GSIPK_consumerId_eserviceId": { + S: makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }), + }, + ":newUpdatedAt": { + S: new Date().toISOString(), + }, }, - }, - UpdateExpression: - "SET purposeState = :newState, purposeVersionId = :newPurposeVersionId, GSIPK_consumerId_eserviceId = :GSIPK_consumerId_eserviceId, updatedAt = :newUpdatedAt" + - agreementUpdateExpression + - descriptorUpdateExpression, - TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - ReturnValues: "NONE", - }; - const command = new UpdateItemCommand(input); - await dynamoDBClient.send(command); - } + UpdateExpression: + "SET purposeState = :newState, purposeVersionId = :newPurposeVersionId, GSIPK_consumerId_eserviceId = :GSIPK_consumerId_eserviceId, updatedAt = :newUpdatedAt" + + agreementUpdateExpression + + descriptorUpdateExpression, + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + } + + if (result.lastEvaluatedKey) { + await runPaginatedUpdateQuery( + dynamoDBClient, + purpose, + purposeState, + purposeVersionId, + result.lastEvaluatedKey + ); + } + }; - if (result.lastEvaluatedKey) { - await runPaginatedUpdateQuery( - dynamoDBClient, - purpose, - purposeState, - purposeVersionId, - result.lastEvaluatedKey - ); - } + await runPaginatedUpdateQuery( + dynamoDBClient, + purpose, + purposeState, + purposeVersionId + ); }; - await runPaginatedUpdateQuery( - dynamoDBClient, - purpose, - purposeState, - purposeVersionId - ); -}; - -export const updatePurposeDataInTokenEntries = async ({ +export const updatePurposeDataInTokenGenStatesEntries = async ({ dynamoDBClient, purposeId, purposeState, @@ -395,13 +399,13 @@ export const updatePurposeDataInTokenEntries = async ({ purposeVersionId: PurposeVersionId, exclusiveStartKey?: Record ): Promise => { - const result = await readTokenEntriesByGSIPKPurposeId( + const result = await readTokenGenStatesEntriesByGSIPKPurposeId( dynamoDBClient, purposeId, exclusiveStartKey ); - for (const entry of result.tokenStateEntries) { + for (const entry of result.tokenGenStatesEntries) { const input: UpdateItemInput = { ConditionExpression: "attribute_exists(PK)", Key: { diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts index 74f0f65a9f..87e2e7ba8d 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -15,8 +15,8 @@ import { getMockDescriptor, getMockPurpose, getMockPurposeVersion, - readAllTokenStateItems, - writeTokenStateEntry, + readAllTokenGenStatesItems, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { generateId, @@ -37,12 +37,12 @@ import { PurposeVersionArchivedV1, purposeVersionState, PurposeVersionSuspendedV1, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { buildDynamoDBTables, deleteDynamoDBTables, - getMockTokenStatesClientPurposeEntry, + getMockTokenGenStatesConsumerClient, toPurposeV1, } from "pagopa-interop-commons-test"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; @@ -53,7 +53,7 @@ import { } from "../src/utils.js"; import { config, - readAllTokenEntriesByGSIPKPurposeId, + readAllTokenGenStatesEntriesByGSIPKPurposeId, writeAgreementEntry, writeCatalogEntry, } from "./utils.js"; @@ -109,7 +109,7 @@ describe("integration tests for events V1", () => { ).toBeUndefined(); // token-generation-states - expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); await handleMessageV1(message, dynamoDBClient); @@ -132,7 +132,7 @@ describe("integration tests for events V1", () => { ); // token-generation-states - expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); }); it("should insert the entry in platform states if it doesn't exist and update token generation states", async () => { @@ -166,33 +166,41 @@ describe("integration tests for events V1", () => { expect(previousPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState, GSIPK_consumerId_eserviceId: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -222,29 +230,32 @@ describe("integration tests for events V1", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -287,21 +298,27 @@ describe("integration tests for events V1", () => { // token-generation-states const purposeId = purpose.id; - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); @@ -315,14 +332,17 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -364,23 +384,29 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -431,27 +457,30 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: purposeVersions[1].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: purposeVersions[1].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -490,23 +519,29 @@ describe("integration tests for events V1", () => { await writePlatformPurposeEntry(dynamoDBClient, previousStateEntry); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[1].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[1].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -552,25 +587,28 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -636,14 +674,15 @@ describe("integration tests for events V1", () => { // token-generation-states const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -655,16 +694,20 @@ describe("integration tests for events V1", () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -676,19 +719,25 @@ describe("integration tests for events V1", () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV1(message, dynamoDBClient); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, }); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -700,9 +749,9 @@ describe("integration tests for events V1", () => { descriptorVoucherLifespan: previousDescriptorEntry.descriptorVoucherLifespan, }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -714,11 +763,11 @@ describe("integration tests for events V1", () => { descriptorVoucherLifespan: previousDescriptorEntry.descriptorVoucherLifespan, }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -756,23 +805,29 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -810,13 +865,16 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -852,23 +910,29 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -912,25 +976,28 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -955,33 +1022,41 @@ describe("integration tests for events V1", () => { expect(previousRetrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeVersionId: purposeVersions[0].id, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeVersionId: purposeVersions[0].id, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurposeVersions: PurposeVersion[] = [ { @@ -1021,13 +1096,16 @@ describe("integration tests for events V1", () => { expect(retrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -1065,31 +1143,39 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1125,27 +1211,30 @@ describe("integration tests for events V1", () => { expect(retrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts index acd17dd905..62c1f11b52 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -17,8 +17,8 @@ import { getMockDescriptor, getMockPurpose, getMockPurposeVersion, - readAllTokenStateItems, - writeTokenStateEntry, + readAllTokenGenStatesItems, + writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test/index.js"; import { generateId, @@ -44,10 +44,10 @@ import { PurposeVersionSuspendedByProducerV2, PurposeVersionUnsuspendedByConsumerV2, PurposeVersionUnsuspendedByProducerV2, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, toPurposeV2, } from "pagopa-interop-models"; -import { getMockTokenStatesClientPurposeEntry } from "pagopa-interop-commons-test"; +import { getMockTokenGenStatesConsumerClient } from "pagopa-interop-commons-test"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { getPurposeStateFromPurposeVersions, @@ -58,7 +58,7 @@ import { config, writeAgreementEntry, writeCatalogEntry, - readAllTokenEntriesByGSIPKPurposeId, + readAllTokenGenStatesEntriesByGSIPKPurposeId, } from "./utils.js"; describe("integration tests for events V2", () => { @@ -111,7 +111,7 @@ describe("integration tests for events V2", () => { ).toBeUndefined(); // token-generation-states - expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); await handleMessageV2(message, dynamoDBClient); @@ -134,7 +134,7 @@ describe("integration tests for events V2", () => { ); // token-generation-states - expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); }); it("should insert the entry in platform states if it doesn't exist and update token generation states", async () => { @@ -168,35 +168,43 @@ describe("integration tests for events V2", () => { expect(previousPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -223,29 +231,32 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -288,21 +299,27 @@ describe("integration tests for events V2", () => { // token-generation-states const purposeId = purpose.id; - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -316,14 +333,17 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -368,25 +388,31 @@ describe("integration tests for events V2", () => { // token-generation-states const purposeId = purpose.id; - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -410,29 +436,32 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -498,14 +527,15 @@ describe("integration tests for events V2", () => { // token-generation-states const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -517,16 +547,20 @@ describe("integration tests for events V2", () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -538,19 +572,25 @@ describe("integration tests for events V2", () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, }); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -563,9 +603,9 @@ describe("integration tests for events V2", () => { previousDescriptorEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -578,11 +618,11 @@ describe("integration tests for events V2", () => { previousDescriptorEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -620,23 +660,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -676,13 +722,16 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -719,23 +768,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -787,27 +842,30 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: purposeVersions[1].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: purposeVersions[1].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -845,7 +903,7 @@ describe("integration tests for events V2", () => { ).toBeUndefined(); // token-generation-states - expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); await handleMessageV2(message, dynamoDBClient); @@ -868,7 +926,7 @@ describe("integration tests for events V2", () => { ); // token-generation-states - expect(await readAllTokenStateItems(dynamoDBClient)).toHaveLength(0); + expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); }); it("should insert the entry in platform states if it doesn't exist and update token generation states", async () => { @@ -903,35 +961,43 @@ describe("integration tests for events V2", () => { expect(previousPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -958,29 +1024,32 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1024,21 +1093,27 @@ describe("integration tests for events V2", () => { // token-generation-states const purposeId = purpose.id; - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1052,14 +1127,17 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -1105,25 +1183,31 @@ describe("integration tests for events V2", () => { // token-generation-states const purposeId = purpose.id; - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_consumerId_eserviceId: undefined, GSIPK_eserviceId_descriptorId: undefined, GSIPK_purposeId: purposeId, purposeState: itemState.inactive, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); @@ -1147,29 +1231,32 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1236,14 +1323,15 @@ describe("integration tests for events V2", () => { // token-generation-states const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -1255,16 +1343,20 @@ describe("integration tests for events V2", () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -1276,19 +1368,25 @@ describe("integration tests for events V2", () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); await handleMessageV2(message, dynamoDBClient); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, }); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -1301,9 +1399,9 @@ describe("integration tests for events V2", () => { previousDescriptorEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: purposeVersions[0].id, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -1316,11 +1414,11 @@ describe("integration tests for events V2", () => { previousDescriptorEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1358,23 +1456,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1414,13 +1518,16 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -1456,23 +1563,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1518,25 +1631,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1573,23 +1689,41 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1628,25 +1762,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1684,23 +1821,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1740,13 +1883,16 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -1782,23 +1928,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1844,25 +1996,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -1899,23 +2054,41 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -1954,25 +2127,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2011,23 +2187,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2068,13 +2250,16 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -2111,23 +2296,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2174,25 +2365,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2230,23 +2424,41 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2285,25 +2497,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2342,23 +2557,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2399,13 +2620,16 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - previousTokenStateEntry1, - previousTokenStateEntry2, + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, ]) ); }); @@ -2442,23 +2666,29 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2505,25 +2735,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2561,23 +2794,41 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState, purposeVersionId: purposeVersions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2616,25 +2867,28 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -2672,33 +2926,41 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeVersionId: purposeVersions[0].id, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeVersionId: purposeVersions[0].id, purposeState, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const updatedPurpose: Purpose = { ...purpose, @@ -2735,25 +2997,28 @@ describe("integration tests for events V2", () => { expect(retrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = - { - ...previousTokenStateEntry1, + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.inactive, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); diff --git a/packages/purpose-platformstate-writer/test/utils.test.ts b/packages/purpose-platformstate-writer/test/utils.test.ts index c3e9d45011..bed8a4dad3 100644 --- a/packages/purpose-platformstate-writer/test/utils.test.ts +++ b/packages/purpose-platformstate-writer/test/utils.test.ts @@ -19,7 +19,7 @@ import { PurposeId, PurposeVersion, PurposeVersionId, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, generateId, itemState, makeGSIPKConsumerIdEServiceId, @@ -38,9 +38,9 @@ import { buildDynamoDBTables, deleteDynamoDBTables, getMockPurposeVersion, - writeTokenStateEntry, - getMockTokenStatesClientPurposeEntry, - readAllTokenStateItems, + writeTokenGenStatesConsumerClient, + getMockTokenGenStatesConsumerClient, + readAllTokenGenStatesItems, getMockPurpose, getMockDescriptor, getMockAgreement, @@ -50,17 +50,17 @@ import { getPurposeStateFromPurposeVersions, readPlatformAgreementEntry, readPlatformPurposeEntry, - readTokenEntriesByGSIPKPurposeId, + readTokenGenStatesEntriesByGSIPKPurposeId, updatePurposeDataInPlatformStatesEntry, - updatePurposeDataInTokenEntries, - updateTokenEntriesWithPurposeAndPlatformStatesData, + updatePurposeDataInTokenGenStatesEntries, + updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData, writePlatformPurposeEntry, } from "../src/utils.js"; import { config, writeAgreementEntry, writeCatalogEntry, - readAllTokenEntriesByGSIPKPurposeId, + readAllTokenGenStatesEntriesByGSIPKPurposeId, } from "./utils.js"; describe("utils tests", async () => { @@ -219,54 +219,67 @@ describe("utils tests", async () => { }); }); - describe("readTokenEntriesByGSIPKPurposeId", async () => { + describe("readTokenGenStatesEntriesByGSIPKPurposeId", async () => { it("should return empty array if entries do not exist", async () => { const purposeId: PurposeId = generateId(); - const result = await readTokenEntriesByGSIPKPurposeId( + const result = await readTokenGenStatesEntriesByGSIPKPurposeId( dynamoDBClient, purposeId ); expect(result).toEqual({ - tokenStateEntries: [], + tokenGenStatesEntries: [], lastEvaluatedKey: undefined, }); }); it("should return entries if they exist (no need for pagination)", async () => { const purposeId = generateId(); - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - }; - await writeTokenStateEntry(tokenStateEntry1, dynamoDBClient); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + GSIPK_purposeId: purposeId, + purposeState: itemState.inactive, + purposeVersionId: generateId(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - }; - await writeTokenStateEntry(tokenStateEntry2, dynamoDBClient); + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + GSIPK_purposeId: purposeId, + purposeState: itemState.inactive, + purposeVersionId: generateId(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); - const result = await readTokenEntriesByGSIPKPurposeId( + const result = await readTokenGenStatesEntriesByGSIPKPurposeId( dynamoDBClient, purposeId ); - expect(result.tokenStateEntries).toEqual( - expect.arrayContaining([tokenStateEntry1, tokenStateEntry2]) + expect(result.tokenGenStatesEntries).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, + ]) ); expect(result.lastEvaluatedKey).toBeUndefined(); }); @@ -276,77 +289,99 @@ describe("utils tests", async () => { const tokenEntriesLength = 10; for (let i = 0; i < tokenEntriesLength; i++) { - const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - publicKey: crypto.randomBytes(100000).toString("hex"), - }; - await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + const tokenGenStatesEntryPK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + GSIPK_purposeId: purposeId, + purposeState: itemState.inactive, + purposeVersionId: generateId(), + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); } vi.spyOn(dynamoDBClient, "send"); - const result = await readTokenEntriesByGSIPKPurposeId( + const result = await readTokenGenStatesEntriesByGSIPKPurposeId( dynamoDBClient, purposeId ); expect(dynamoDBClient.send).toHaveBeenCalledTimes(1); - expect(result.tokenStateEntries.length).toBeLessThan(tokenEntriesLength); + expect(result.tokenGenStatesEntries.length).toBeLessThan( + tokenEntriesLength + ); expect(result.lastEvaluatedKey).toBeDefined(); }); }); - describe("readAllTokenEntriesByGSIPKPurposeId", async () => { + describe("readAllTokenGenStatesEntriesByGSIPKPurposeId", async () => { it("should return empty array if entries do not exist", async () => { const purposeId: PurposeId = generateId(); - const tokenEntries = await readAllTokenEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); - expect(tokenEntries).toEqual([]); + const tokenGenStatesConsumerClients = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + expect(tokenGenStatesConsumerClients).toEqual([]); }); it("should return entries if they exist (no need for pagination)", async () => { const purposeId = generateId(); - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenStateEntry1: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - }; - await writeTokenStateEntry(tokenStateEntry1, dynamoDBClient); - - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenStateEntry2: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - }; - await writeTokenStateEntry(tokenStateEntry2, dynamoDBClient); + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + GSIPK_purposeId: purposeId, + purposeState: itemState.inactive, + purposeVersionId: generateId(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenEntries = await readAllTokenEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + GSIPK_purposeId: purposeId, + purposeState: itemState.inactive, + purposeVersionId: generateId(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient ); - expect(tokenEntries).toEqual( - expect.arrayContaining([tokenStateEntry1, tokenStateEntry2]) + const tokenGenStatesConsumerClients = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + + expect(tokenGenStatesConsumerClients).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, + ]) ); }); @@ -354,33 +389,42 @@ describe("utils tests", async () => { const purposeId = generateId(); const tokenEntriesLength = 10; - const writtenEntries: TokenGenerationStatesClientPurposeEntry[] = []; + const writtenTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = + []; for (let i = 0; i < tokenEntriesLength; i++) { - const tokenStateEntryPK = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenStateEntry: TokenGenerationStatesClientPurposeEntry = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - publicKey: crypto.randomBytes(100000).toString("hex"), - }; - await writeTokenStateEntry(tokenStateEntry, dynamoDBClient); + const tokenGenStatesEntryPK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + GSIPK_purposeId: purposeId, + purposeState: itemState.inactive, + purposeVersionId: generateId(), + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); // eslint-disable-next-line functional/immutable-data - writtenEntries.push(tokenStateEntry); + writtenTokenGenStatesConsumerClients.push(tokenGenStatesConsumerClient); } vi.spyOn(dynamoDBClient, "send"); - const tokenEntries = await readAllTokenEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const tokenGenStatesConsumerClients = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); - expect(tokenEntries).toHaveLength(tokenEntriesLength); - expect(tokenEntries).toEqual(expect.arrayContaining(writtenEntries)); + expect(tokenGenStatesConsumerClients).toHaveLength(tokenEntriesLength); + expect(tokenGenStatesConsumerClients).toEqual( + expect.arrayContaining(writtenTokenGenStatesConsumerClients) + ); }); }); @@ -557,25 +601,27 @@ describe("utils tests", async () => { describe("updatePurposeDataInTokenGenerationStatesTable", async () => { it("should do nothing if previous entries don't exist", async () => { - const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); - expect(tokenStateEntries).toEqual([]); + const tokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + expect(tokenGenStatesEntries).toEqual([]); const purpose: Purpose = { ...getMockPurpose(), versions: [getMockPurposeVersion()], }; await expect( - updatePurposeDataInTokenEntries({ + updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState: itemState.inactive, purposeVersionId: purpose.versions[0].id, }) ).resolves.not.toThrowError(); - const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(tokenStateEntriesAfterUpdate).toEqual([]); + expect(tokenGenStatesEntriesAfterUpdate).toEqual([]); }); it("should update state and purpose version id if previous entries exist", async () => { @@ -584,62 +630,73 @@ describe("utils tests", async () => { versions: [getMockPurposeVersion()], }; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: purpose.id, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purpose.id, purposeState: itemState.inactive, purposeVersionId: purpose.versions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: purpose.id, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purpose.id, purposeState: itemState.inactive, purposeVersionId: purpose.versions[0].id, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const newPurposeVersionId = generateId(); - await updatePurposeDataInTokenEntries({ + await updatePurposeDataInTokenGenStatesEntries({ dynamoDBClient, purposeId: purpose.id, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purpose.id); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purpose.id + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -647,28 +704,30 @@ describe("utils tests", async () => { describe("updatePurposeEntriesInTokenGenerationStatesTable", async () => { it("should do nothing if previous entries don't exist", async () => { - const tokenStateEntries = await readAllTokenStateItems(dynamoDBClient); - expect(tokenStateEntries).toEqual([]); + const tokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + expect(tokenGenStatesEntries).toEqual([]); const purpose: Purpose = { ...getMockPurpose(), versions: [getMockPurposeVersion()], }; await expect( - updateTokenEntriesWithPurposeAndPlatformStatesData( + updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( dynamoDBClient, purpose, itemState.inactive, purpose.versions[0].id ) ).resolves.not.toThrowError(); - const tokenStateEntriesAfterUpdate = await readAllTokenStateItems( + const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(tokenStateEntriesAfterUpdate).toEqual([]); + expect(tokenGenStatesEntriesAfterUpdate).toEqual([]); }); - it("should update entries with purpose state and version id if corresponding platform states entries don't exist", async () => { + it("should update entries with just purpose state and version id, if descriptor and agreement platform states entries don't exist", async () => { const purpose: Purpose = { ...getMockPurpose(), versions: [getMockPurposeVersion()], @@ -676,14 +735,15 @@ describe("utils tests", async () => { // token-generation-states const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -694,16 +754,20 @@ describe("utils tests", async () => { descriptorAudience: undefined, descriptorVoucherLifespan: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -715,10 +779,13 @@ describe("utils tests", async () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const newPurposeVersionId = generateId(); - await updateTokenEntriesWithPurposeAndPlatformStatesData( + await updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( dynamoDBClient, purpose, itemState.active, @@ -729,29 +796,33 @@ describe("utils tests", async () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -788,40 +859,48 @@ describe("utils tests", async () => { // token-generation-states const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, agreementId: undefined, agreementState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, agreementId: undefined, agreementState: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const newPurposeVersionId = generateId(); - await updateTokenEntriesWithPurposeAndPlatformStatesData( + await updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( dynamoDBClient, purpose, itemState.active, @@ -832,11 +911,14 @@ describe("utils tests", async () => { eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, }); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, @@ -844,9 +926,9 @@ describe("utils tests", async () => { agreementId: mockAgreement.id, agreementState: previousAgreementEntry.state, }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, @@ -854,11 +936,11 @@ describe("utils tests", async () => { agreementId: mockAgreement.id, agreementState: previousAgreementEntry.state, }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); @@ -909,14 +991,15 @@ describe("utils tests", async () => { // token-generation-states const purposeId = purpose.id; - const tokenStateEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK1), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -927,16 +1010,20 @@ describe("utils tests", async () => { descriptorAudience: undefined, descriptorVoucherLifespan: undefined, }; - await writeTokenStateEntry(previousTokenStateEntry1, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); - const tokenStateEntryPK2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const previousTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId, + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { - ...getMockTokenStatesClientPurposeEntry(tokenStateEntryPK2), + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), GSIPK_purposeId: purposeId, purposeState: itemState.inactive, GSIPK_consumerId_eserviceId: undefined, @@ -948,26 +1035,32 @@ describe("utils tests", async () => { descriptorVoucherLifespan: undefined, updatedAt: new Date().toISOString(), }; - await writeTokenStateEntry(previousTokenStateEntry2, dynamoDBClient); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); const newPurposeVersionId = generateId(); - await updateTokenEntriesWithPurposeAndPlatformStatesData( + await updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( dynamoDBClient, purpose, itemState.active, newPurposeVersionId ); - const retrievedTokenStateEntries = - await readAllTokenEntriesByGSIPKPurposeId(dynamoDBClient, purposeId); + const retrievedTokenGenStatesEntries = + await readAllTokenGenStatesEntriesByGSIPKPurposeId( + dynamoDBClient, + purposeId + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, }); - const expectedTokenStateEntry1: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry1, + ...tokenGenStatesConsumerClient1, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -980,9 +1073,9 @@ describe("utils tests", async () => { previousDescriptorEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - const expectedTokenStateEntry2: TokenGenerationStatesClientPurposeEntry = + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { - ...previousTokenStateEntry2, + ...tokenGenStatesConsumerClient2, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, @@ -995,11 +1088,11 @@ describe("utils tests", async () => { previousDescriptorEntry.descriptorVoucherLifespan, updatedAt: new Date().toISOString(), }; - expect(retrievedTokenStateEntries).toHaveLength(2); - expect(retrievedTokenStateEntries).toEqual( + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - expectedTokenStateEntry1, - expectedTokenStateEntry2, + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, ]) ); }); diff --git a/packages/purpose-platformstate-writer/test/utils.ts b/packages/purpose-platformstate-writer/test/utils.ts index da9f810035..eab500ebda 100644 --- a/packages/purpose-platformstate-writer/test/utils.ts +++ b/packages/purpose-platformstate-writer/test/utils.ts @@ -13,11 +13,11 @@ import { PlatformStatesAgreementEntry, PlatformStatesCatalogEntry, PurposeId, - TokenGenerationStatesClientPurposeEntry, + TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { inject } from "vitest"; import { unmarshall } from "@aws-sdk/util-dynamodb"; -import { readTokenEntriesByGSIPKPurposeId } from "../src/utils.js"; +import { readTokenGenStatesEntriesByGSIPKPurposeId } from "../src/utils.js"; export const config = inject("tokenGenerationReadModelConfig"); @@ -131,25 +131,25 @@ export const writeCatalogEntry = async ( await dynamoDBClient.send(command); }; -export const readAllTokenEntriesByGSIPKPurposeId = async ( +export const readAllTokenGenStatesEntriesByGSIPKPurposeId = async ( dynamoDBClient: DynamoDBClient, purposeId: PurposeId -): Promise => { +): Promise => { const runPaginatedQuery = async ( dynamoDBClient: DynamoDBClient, purposeId: PurposeId, exclusiveStartKey?: Record - ): Promise => { - const result = await readTokenEntriesByGSIPKPurposeId( + ): Promise => { + const result = await readTokenGenStatesEntriesByGSIPKPurposeId( dynamoDBClient, purposeId, exclusiveStartKey ); if (!result.lastEvaluatedKey) { - return result.tokenStateEntries; + return result.tokenGenStatesEntries; } else { return [ - ...result.tokenStateEntries, + ...result.tokenGenStatesEntries, ...(await runPaginatedQuery( dynamoDBClient, purposeId, From 7833f6d746c2d7f6b02f8f618983c2250e824011 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:16:18 +0100 Subject: [PATCH 066/126] Fix DynamoDB GSIs projection types (#1257) Co-authored-by: Roberto Taglioni --- .../schema/platform-states-dynamo-db.json | 40 +++- .../token-generation-states-dynamo-db.json | 127 ++++++++++-- .../src/index.ts | 2 +- .../src/utils.ts | 45 ++-- .../consumerServiceV1.integration.test.ts | 132 +++++------- .../consumerServiceV2.integration.test.ts | 188 ++++++----------- .../test/utils.test.ts | 46 ++--- .../test/utils.ts | 10 +- .../src/consumerServiceV1.ts | 2 +- .../src/consumerServiceV2.ts | 2 +- .../src/index.ts | 2 +- .../src/utils.ts | 109 ++++++---- .../consumerServiceV1.integration.test.ts | 10 +- .../consumerServiceV2.integration.test.ts | 10 +- .../test/utils.test.ts | 30 +-- .../test/utils.ts | 10 +- .../src/routers/AuthorizationServerRouter.ts | 2 +- .../catalog-platformstate-writer/src/utils.ts | 42 ++-- .../test/consumerServiceV1.test.ts | 55 ++--- .../test/consumerServiceV2.test.ts | 124 ++++------- .../test/utils.test.ts | 54 +++-- .../test/utils.ts | 10 +- .../commons-test/src/setupDynamoDBtables.ts | 54 +++-- .../src/tokenGenerationReadmodelUtils.ts | 22 +- .../platform-states-entry.ts | 13 ++ .../token-generation-states-entry.ts | 117 +++++++++++ .../purpose-platformstate-writer/src/utils.ts | 32 +-- .../consumerServiceV1.integration.test.ts | 84 +++----- .../consumerServiceV2.integration.test.ts | 195 +++++++----------- .../test/utils.test.ts | 175 +++------------- .../test/utils.ts | 46 +---- 31 files changed, 857 insertions(+), 933 deletions(-) diff --git a/docker/dynamo-db/schema/platform-states-dynamo-db.json b/docker/dynamo-db/schema/platform-states-dynamo-db.json index 6a0d2fab73..cf43152c90 100644 --- a/docker/dynamo-db/schema/platform-states-dynamo-db.json +++ b/docker/dynamo-db/schema/platform-states-dynamo-db.json @@ -1,19 +1,45 @@ { "TableName": "platform-states", "AttributeDefinitions": [ - { "AttributeName": "PK", "AttributeType": "S" }, - { "AttributeName": "GSIPK_consumerId_eserviceId", "AttributeType": "S" }, - { "AttributeName": "GSISK_agreementTimestamp", "AttributeType": "S" } + { + "AttributeName": "PK", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_consumerId_eserviceId", + "AttributeType": "S" + }, + { + "AttributeName": "GSISK_agreementTimestamp", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "PK", + "KeyType": "HASH" + } ], - "KeySchema": [{ "AttributeName": "PK", "KeyType": "HASH" }], "GlobalSecondaryIndexes": [ { "IndexName": "Agreement", "KeySchema": [ - { "AttributeName": "GSIPK_consumerId_eserviceId", "KeyType": "HASH" }, - { "AttributeName": "GSISK_agreementTimestamp", "KeyType": "RANGE" } + { + "AttributeName": "GSIPK_consumerId_eserviceId", + "KeyType": "HASH" + }, + { + "AttributeName": "GSISK_agreementTimestamp", + "KeyType": "RANGE" + } ], - "Projection": { "ProjectionType": "ALL" } + "Projection": { + "ProjectionType": "INCLUDE", + "NonKeyAttributes": [ + "state", + "agreementDescriptorId" + ] + } } ], "ProvisionedThroughput": { diff --git a/docker/dynamo-db/schema/token-generation-states-dynamo-db.json b/docker/dynamo-db/schema/token-generation-states-dynamo-db.json index 55dfd92b97..319a6fa58d 100644 --- a/docker/dynamo-db/schema/token-generation-states-dynamo-db.json +++ b/docker/dynamo-db/schema/token-generation-states-dynamo-db.json @@ -1,22 +1,53 @@ { "TableName": "token-generation-states", "AttributeDefinitions": [ - { "AttributeName": "PK", "AttributeType": "S" }, - { "AttributeName": "GSIPK_eserviceId_descriptorId", "AttributeType": "S" }, - { "AttributeName": "GSIPK_consumerId_eserviceId", "AttributeType": "S" }, - { "AttributeName": "GSIPK_purposeId", "AttributeType": "S" }, - { "AttributeName": "GSIPK_clientId", "AttributeType": "S" }, - { "AttributeName": "GSIPK_kid", "AttributeType": "S" }, - { "AttributeName": "GSIPK_clientId_purposeId", "AttributeType": "S" } + { + "AttributeName": "PK", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_eserviceId_descriptorId", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_consumerId_eserviceId", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_purposeId", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_clientId", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_kid", + "AttributeType": "S" + }, + { + "AttributeName": "GSIPK_clientId_purposeId", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "PK", + "KeyType": "HASH" + } ], - "KeySchema": [{ "AttributeName": "PK", "KeyType": "HASH" }], "GlobalSecondaryIndexes": [ { "IndexName": "Descriptor", "KeySchema": [ - { "AttributeName": "GSIPK_eserviceId_descriptorId", "KeyType": "HASH" } + { + "AttributeName": "GSIPK_eserviceId_descriptorId", + "KeyType": "HASH" + } ], - "Projection": { "ProjectionType": "ALL" } + "Projection": { + "ProjectionType": "KEYS_ONLY" + } }, { "IndexName": "Agreement", @@ -26,29 +57,87 @@ "KeyType": "HASH" } ], - "Projection": { "ProjectionType": "ALL" } + "Projection": { + "ProjectionType": "INCLUDE", + "NonKeyAttributes": [ + "agreementState", + "descriptorState", + "descriptorAudience", + "descriptorVoucherLifespan" + ] + } }, { "IndexName": "Purpose", - "KeySchema": [{ "AttributeName": "GSIPK_purposeId", "KeyType": "HASH" }], - "Projection": { "ProjectionType": "ALL" } + "KeySchema": [ + { + "AttributeName": "GSIPK_purposeId", + "KeyType": "HASH" + } + ], + "Projection": { + "ProjectionType": "INCLUDE", + "NonKeyAttributes": [ + "agreementId", + "agreementState", + "GSIPK_eserviceId_descriptorId", + "descriptorAudience", + "descriptorState", + "descriptorVoucherLifespan", + "purposeState", + "purposeVersionId" + ] + } }, { "IndexName": "Client", - "KeySchema": [{ "AttributeName": "GSIPK_clientId", "KeyType": "HASH" }], - "Projection": { "ProjectionType": "ALL" } + "KeySchema": [ + { + "AttributeName": "GSIPK_clientId", + "KeyType": "HASH" + } + ], + "Projection": { + "ProjectionType": "INCLUDE", + "NonKeyAttributes": [ + "consumerId", + "clientKind", + "publicKey", + "GSIPK_kid" + ] + } }, { "IndexName": "Kid", - "KeySchema": [{ "AttributeName": "GSIPK_kid", "KeyType": "HASH" }], - "Projection": { "ProjectionType": "ALL" } + "KeySchema": [ + { + "AttributeName": "GSIPK_kid", + "KeyType": "HASH" + } + ], + "Projection": { + "ProjectionType": "KEYS_ONLY" + } }, { "IndexName": "ClientPurpose", "KeySchema": [ - { "AttributeName": "GSIPK_clientId_purposeId", "KeyType": "HASH" } + { + "AttributeName": "GSIPK_clientId_purposeId", + "KeyType": "HASH" + } ], - "Projection": { "ProjectionType": "ALL" } + "Projection": { + "ProjectionType": "INCLUDE", + "NonKeyAttributes": [ + "GSIPK_clientId", + "GSIPK_kid", + "GSIPK_purposeId", + "consumerId", + "clientKind", + "publicKey" + ] + } } ], "ProvisionedThroughput": { diff --git a/packages/agreement-platformstate-writer/src/index.ts b/packages/agreement-platformstate-writer/src/index.ts index b87807810a..a0466dd8b3 100644 --- a/packages/agreement-platformstate-writer/src/index.ts +++ b/packages/agreement-platformstate-writer/src/index.ts @@ -13,7 +13,7 @@ import { handleMessageV1 } from "./consumerServiceV1.js"; import { handleMessageV2 } from "./consumerServiceV2.js"; import { config } from "./config/config.js"; -const dynamoDBClient = new DynamoDBClient({}); +const dynamoDBClient = new DynamoDBClient(); async function processMessage({ message, partition, diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index d669123ae8..dac5b945d5 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -11,7 +11,8 @@ import { PlatformStatesAgreementPK, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, - TokenGenerationStatesConsumerClient, + PlatformStatesAgreementGSIAgreement, + TokenGenStatesConsumerClientGSIAgreement, } from "pagopa-interop-models"; import { AttributeValue, @@ -89,7 +90,7 @@ export const readAgreementEntry = async ( if (!agreementEntry.success) { throw genericInternalError( - `Unable to parse agreement entry item: result ${JSON.stringify( + `Unable to parse platform-states agreement entry: result ${JSON.stringify( agreementEntry )} - data ${JSON.stringify(data)} ` ); @@ -156,7 +157,7 @@ export const updateAgreementStateOnTokenGenStatesEntries = async ({ agreementState, dynamoDBClient, }: { - entriesToUpdate: TokenGenerationStatesConsumerClient[]; + entriesToUpdate: TokenGenStatesConsumerClientGSIAgreement[]; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; }): Promise => { @@ -196,7 +197,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = GSIPK_eserviceId_descriptorId, catalogEntry, }: { - entriesToUpdate: TokenGenerationStatesConsumerClient[]; + entriesToUpdate: TokenGenStatesConsumerClientGSIAgreement[]; agreementId: AgreementId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; @@ -266,12 +267,12 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNamePlatform, IndexName: "Agreement", @@ -287,7 +288,7 @@ export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( if (!data.Items) { throw genericInternalError( - `Unable to read platform state agreement entries: result ${JSON.stringify( + `Unable to read platform-states agreement entries: result ${JSON.stringify( data )} ` ); @@ -295,12 +296,12 @@ export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const agreementEntries = z - .array(PlatformStatesAgreementEntry) + .array(PlatformStatesAgreementGSIAgreement) .safeParse(unmarshalledItems); if (!agreementEntries.success) { throw genericInternalError( - `Unable to parse platform state entry items: result ${JSON.stringify( + `Unable to parse platform-states agreement entries: result ${JSON.stringify( agreementEntries )} - data ${JSON.stringify(data)} ` ); @@ -342,12 +343,12 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ dynamoDBClient: DynamoDBClient; GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; catalogEntry: PlatformStatesCatalogEntry | undefined; -}): Promise => { +}): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Agreement", @@ -362,18 +363,20 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIAgreement) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -435,12 +438,12 @@ export const updateAgreementStateOnTokenGenStates = async ({ GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; -}): Promise => { +}): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Agreement", @@ -455,18 +458,20 @@ export const updateAgreementStateOnTokenGenStates = async ({ if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIAgreement) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -521,7 +526,7 @@ export const readCatalogEntry = async ( if (!catalogEntry.success) { throw genericInternalError( - `Unable to parse catalog entry item: result ${JSON.stringify( + `Unable to parse platform-states catalog entry: result ${JSON.stringify( catalogEntry )} - data ${JSON.stringify(data)} ` ); diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 230c36c68f..123f6e3463 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; import { afterAll, afterEach, @@ -31,29 +30,22 @@ import { makePlatformStatesEServiceDescriptorPK, makeTokenGenerationStatesClientKidPurposePK, } from "pagopa-interop-models"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { getMockTokenGenStatesConsumerClient, getMockAgreement, buildDynamoDBTables, deleteDynamoDBTables, toAgreementV1, - readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId, getMockPlatformStatesAgreementEntry, writePlatformCatalogEntry, writeTokenGenStatesConsumerClient, + readAllTokenGenStatesItems, } from "pagopa-interop-commons-test"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests V1 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -152,11 +144,9 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -259,11 +249,9 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { @@ -385,11 +373,9 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -531,11 +517,9 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -713,11 +697,9 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -824,11 +806,9 @@ describe("integration tests V1 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -985,11 +965,9 @@ describe("integration tests V1 events", async () => { eserviceId: latestAgreement.eserviceId, descriptorId: latestAgreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { @@ -1157,11 +1135,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1287,11 +1263,9 @@ describe("integration tests V1 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1483,11 +1457,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1632,11 +1604,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1826,11 +1796,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1975,11 +1943,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2128,11 +2094,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2272,11 +2236,9 @@ describe("integration tests V1 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index d6b41f15b2..affd7e45fe 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; import { afterAll, afterEach, @@ -38,28 +37,21 @@ import { makeTokenGenerationStatesClientKidPurposePK, toAgreementV2, } from "pagopa-interop-models"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { getMockTokenGenStatesConsumerClient, getMockAgreement, buildDynamoDBTables, deleteDynamoDBTables, - readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId, writeTokenGenStatesConsumerClient, getMockPlatformStatesAgreementEntry, writePlatformCatalogEntry, + readAllTokenGenStatesItems, } from "pagopa-interop-commons-test"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests V2 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -158,11 +150,9 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -265,11 +255,9 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -391,11 +379,9 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -537,11 +523,9 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -720,11 +704,9 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -898,11 +880,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1039,11 +1019,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1230,11 +1208,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1371,11 +1347,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1562,11 +1536,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1703,11 +1675,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1894,11 +1864,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -2035,11 +2003,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2227,11 +2193,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -2368,11 +2332,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2493,11 +2455,9 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -2654,11 +2614,9 @@ describe("integration tests V2 events", async () => { eserviceId, descriptorId: latestAgreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { @@ -2826,11 +2784,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedAgreementEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -2956,11 +2912,9 @@ describe("integration tests V2 events", async () => { eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -3082,11 +3036,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -3217,11 +3169,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -3361,11 +3311,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index 9f9c8ee651..cdd9bb2e0c 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -1,10 +1,6 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; import crypto from "crypto"; -import { - ConditionalCheckFailedException, - DynamoDBClient, -} from "@aws-sdk/client-dynamodb"; +import { ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb"; import { buildDynamoDBTables, deleteDynamoDBTables, @@ -30,6 +26,7 @@ import { PlatformStatesCatalogEntry, TenantId, TokenGenerationStatesConsumerClient, + TokenGenStatesConsumerClientGSIAgreement, } from "pagopa-interop-models"; import { afterAll, @@ -41,6 +38,7 @@ import { it, vi, } from "vitest"; +import { z } from "zod"; import { updateAgreementStateInPlatformStatesEntry, readAgreementEntry, @@ -51,15 +49,9 @@ import { updateAgreementStateAndDescriptorInfoOnTokenGenStates, isLatestAgreement, } from "../src/utils.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("utils", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -319,8 +311,12 @@ describe("utils", async () => { expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenGenStatesConsumerClient1, - tokenGenStatesConsumerClient2, + TokenGenStatesConsumerClientGSIAgreement.parse( + tokenGenStatesConsumerClient1 + ), + TokenGenStatesConsumerClientGSIAgreement.parse( + tokenGenStatesConsumerClient2 + ), ]) ); }); @@ -368,7 +364,11 @@ describe("utils", async () => { expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); expect(tokenGenStatesConsumerClients).toHaveLength(tokenEntriesLength); expect(tokenGenStatesConsumerClients).toEqual( - expect.arrayContaining(writtenTokenGenStatesConsumerClients) + expect.arrayContaining( + z + .array(TokenGenStatesConsumerClientGSIAgreement) + .parse(writtenTokenGenStatesConsumerClients) + ) ); }); }); @@ -441,11 +441,9 @@ describe("utils", async () => { agreementState: agreementState.active, dynamoDBClient, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -592,11 +590,9 @@ describe("utils", async () => { GSIPK_eserviceId_descriptorId, catalogEntry, }); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, diff --git a/packages/agreement-platformstate-writer/test/utils.ts b/packages/agreement-platformstate-writer/test/utils.ts index aca07c9cc6..b445e6374b 100644 --- a/packages/agreement-platformstate-writer/test/utils.ts +++ b/packages/agreement-platformstate-writer/test/utils.ts @@ -1,3 +1,11 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { inject } from "vitest"; -export const config = inject("tokenGenerationReadModelConfig"); +const config = inject("tokenGenerationReadModelConfig"); + +if (!config) { + throw new Error("config is not defined"); +} +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, +}); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 4f1b9fd453..d0317f231a 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -323,7 +323,7 @@ export async function handleMessageV1( dynamoDBClient ); await deleteClientEntryFromTokenGenerationStates( - entry, + entry.PK, dynamoDBClient ); return newTokenGenStatesConsumerClient; diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 1f0462d9fa..f8f3447f1e 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -305,7 +305,7 @@ export async function handleMessageV2( dynamoDBClient ); await deleteClientEntryFromTokenGenerationStates( - entry, + entry.PK, dynamoDBClient ); return newTokenGenStatesConsumerClient; diff --git a/packages/authorization-platformstate-writer/src/index.ts b/packages/authorization-platformstate-writer/src/index.ts index 661f322d2d..33da997245 100644 --- a/packages/authorization-platformstate-writer/src/index.ts +++ b/packages/authorization-platformstate-writer/src/index.ts @@ -13,7 +13,7 @@ import { handleMessageV1 } from "./consumerServiceV1.js"; import { handleMessageV2 } from "./consumerServiceV2.js"; import { config } from "./config/config.js"; -const dynamoDBClient = new DynamoDBClient({}); +const dynamoDBClient = new DynamoDBClient(); async function processMessage({ message, partition, diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 74bc4788c2..fb7ce4f438 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -11,7 +11,10 @@ import { QueryCommand, QueryCommandOutput, QueryInput, + UpdateItemCommand, + UpdateItemInput, } from "@aws-sdk/client-dynamodb"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; import { AgreementId, ClientId, @@ -30,8 +33,8 @@ import { makePlatformStatesPurposePK, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, - PlatformStatesAgreementEntry, PlatformStatesAgreementPK, + PlatformStatesAgreementGSIAgreement, PlatformStatesCatalogEntry, PlatformStatesClientEntry, PlatformStatesClientPK, @@ -43,13 +46,13 @@ import { TokenGenerationStatesClientKidPK, TokenGenerationStatesClientKidPurposePK, TokenGenerationStatesConsumerClient, - TokenGenerationStatesGenericClient, + TokenGenStatesConsumerClientGSIClient, + TokenGenStatesConsumerClientGSIClientPurpose, + TokenGenStatesGenericClientGSIClient, + TokenGenStatesGenericClientGSIKid, } from "pagopa-interop-models"; -import { z } from "zod"; -import { unmarshall } from "@aws-sdk/util-dynamodb"; import { match } from "ts-pattern"; -import { UpdateItemInput } from "@aws-sdk/client-dynamodb"; -import { UpdateItemCommand } from "@aws-sdk/client-dynamodb"; +import { z } from "zod"; import { config } from "./config/config.js"; export const deleteEntriesFromTokenGenStatesByKid = async ( @@ -74,25 +77,30 @@ export const deleteEntriesFromTokenGenStatesByKid = async ( const data: QueryCommandOutput = await dynamoDBClient.send(command); if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesGenericClient) + .array(TokenGenStatesGenericClientGSIKid) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } for (const entry of tokenGenStatesEntries.data) { - await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient + ); } if (data.LastEvaluatedKey) { @@ -123,11 +131,11 @@ export const deleteClientEntryFromPlatformStates = async ( }; export const deleteEntriesFromTokenGenStatesByClientId = async ( - GSIPK_client: ClientId, + GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient ): Promise => { const runPaginatedQuery = async ( - GSIPK_client: ClientId, + GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { @@ -136,7 +144,7 @@ export const deleteEntriesFromTokenGenStatesByClientId = async ( IndexName: "Client", KeyConditionExpression: `GSIPK_clientId = :gsiValue`, ExpressionAttributeValues: { - ":gsiValue": { S: GSIPK_client }, + ":gsiValue": { S: GSIPK_clientId }, }, ExclusiveStartKey: exclusiveStartKey, }; @@ -145,30 +153,35 @@ export const deleteEntriesFromTokenGenStatesByClientId = async ( if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesGenericClient) + .array(TokenGenStatesGenericClientGSIClient) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); } for (const entry of tokenGenStatesEntries.data) { - await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient + ); } if (data.LastEvaluatedKey) { await runPaginatedQuery( - GSIPK_client, + GSIPK_clientId, dynamoDBClient, data.LastEvaluatedKey ); @@ -176,16 +189,18 @@ export const deleteEntriesFromTokenGenStatesByClientId = async ( } }; - await runPaginatedQuery(GSIPK_client, dynamoDBClient, undefined); + await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); }; export const deleteClientEntryFromTokenGenerationStates = async ( - entryToDelete: TokenGenerationStatesGenericClient, + entryToDeletePK: + | TokenGenerationStatesClientKidPK + | TokenGenerationStatesClientKidPurposePK, dynamoDBClient: DynamoDBClient ): Promise => { const input: DeleteItemInput = { Key: { - PK: { S: entryToDelete.PK }, + PK: { S: entryToDeletePK }, }, TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; @@ -214,7 +229,7 @@ export const readPlatformClientEntry = async ( if (!clientEntry.success) { throw genericInternalError( - `Unable to parse client entry item: result ${JSON.stringify( + `Unable to parse platform-states client entry: result ${JSON.stringify( clientEntry )} - data ${JSON.stringify(data)} ` ); @@ -228,7 +243,7 @@ const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise<{ - tokenGenStatesEntries: TokenGenerationStatesConsumerClient[]; + tokenGenStatesEntries: TokenGenStatesConsumerClientGSIClientPurpose[]; lastEvaluatedKey: Record | undefined; }> => { const input: QueryInput = { @@ -245,18 +260,20 @@ const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIClientPurpose) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -284,7 +301,10 @@ export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( ); for (const entry of res.tokenGenStatesEntries) { - await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient + ); } if (res.lastEvaluatedKey) { @@ -306,7 +326,7 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurpose( GSIPK_clientId_purposeId, dynamoDBClient, @@ -332,7 +352,10 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( await writeTokenGenStatesConsumerClient(newEntry, dynamoDBClient); // delete the old one - await deleteClientEntryFromTokenGenerationStates(entry, dynamoDBClient); + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient + ); } if (!res.lastEvaluatedKey) { @@ -407,7 +430,7 @@ export const readPlatformCatalogEntry = async ( if (!catalogEntry.success) { throw genericInternalError( - `Unable to parse catalog entry item: result ${JSON.stringify( + `Unable to parse platform-states catalog entry: result ${JSON.stringify( catalogEntry )} - data ${JSON.stringify(data)} ` ); @@ -419,7 +442,7 @@ export const readPlatformCatalogEntry = async ( export const readPlatformAgreementEntryByGSIPKConsumerIdEServiceId = async ( gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNamePlatform, IndexName: "Agreement", @@ -437,14 +460,14 @@ export const readPlatformAgreementEntryByGSIPKConsumerIdEServiceId = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const platformAgreementEntries = z - .array(PlatformStatesAgreementEntry) + .array(PlatformStatesAgreementGSIAgreement) .safeParse(unmarshalledItems); if (platformAgreementEntries.success) { return platformAgreementEntries.data[0]; } else { throw genericInternalError( - `Unable to parse platform agreement entries: result ${JSON.stringify( + `Unable to parse platform-states agreement entries: result ${JSON.stringify( platformAgreementEntries )} ` ); @@ -473,7 +496,7 @@ export const readPlatformPurposeEntry = async ( if (!purposeEntry.success) { throw genericInternalError( - `Unable to parse purpose entry item: result ${JSON.stringify( + `Unable to parse platform-states purpose entry: result ${JSON.stringify( purposeEntry )} - data ${JSON.stringify(data)} ` ); @@ -761,12 +784,12 @@ export const writePlatformClientEntry = async ( export const readConsumerClientEntriesInTokenGenerationStates = async ( GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Client", @@ -782,7 +805,7 @@ export const readConsumerClientEntriesInTokenGenerationStates = async ( if (!data.Items) { throw genericInternalError( - `Unable to read platform state client entries: result ${JSON.stringify( + `Unable to read token-generation-states client entries: result ${JSON.stringify( data )} ` ); @@ -790,12 +813,12 @@ export const readConsumerClientEntriesInTokenGenerationStates = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const clientEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIClient) .safeParse(unmarshalledItems); if (!clientEntries.success) { throw genericInternalError( - `Unable to parse token state entry items: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( clientEntries )} - data ${JSON.stringify(data)} ` ); @@ -886,7 +909,7 @@ export const retrievePlatformStatesByPurpose = async ( dynamoDBClient: DynamoDBClient ): Promise<{ purposeEntry?: PlatformStatesPurposeEntry; - agreementEntry?: PlatformStatesAgreementEntry; + agreementEntry?: PlatformStatesAgreementGSIAgreement; catalogEntry?: PlatformStatesCatalogEntry; }> => { const purposePK = makePlatformStatesPurposePK(purposeId); @@ -1022,7 +1045,7 @@ export const updateTokenGenStatesDataForSecondRetrieval = async ({ dynamoDBClient: DynamoDBClient; entry: TokenGenerationStatesConsumerClient; purposeEntry?: PlatformStatesPurposeEntry; - agreementEntry?: PlatformStatesAgreementEntry; + agreementEntry?: PlatformStatesAgreementGSIAgreement; catalogEntry?: PlatformStatesCatalogEntry; }): Promise => { const setIfChanged = ( @@ -1173,12 +1196,12 @@ export const createTokenGenStatesConsumerClient = ({ agreementEntry, catalogEntry, }: { - tokenGenStatesClient: TokenGenerationStatesConsumerClient; + tokenGenStatesClient: TokenGenStatesConsumerClientGSIClient; kid: string; clientId: ClientId; purposeId: PurposeId; purposeEntry?: PlatformStatesPurposeEntry; - agreementEntry?: PlatformStatesAgreementEntry; + agreementEntry?: PlatformStatesAgreementGSIAgreement; catalogEntry?: PlatformStatesCatalogEntry; }): TokenGenerationStatesConsumerClient => { const pk = makeTokenGenerationStatesClientKidPurposePK({ diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index ea3bf680e6..d3c04a176f 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -1,4 +1,3 @@ -import { fail } from "assert"; import { afterAll, afterEach, @@ -9,7 +8,6 @@ import { it, vi, } from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { buildDynamoDBTables, deleteDynamoDBTables, @@ -74,15 +72,9 @@ import { writePlatformClientEntry, writeTokenGenStatesApiClient, } from "../src/utils.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests V1 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index 1cbb8c2d5d..60c2227998 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -1,4 +1,3 @@ -import { fail } from "assert"; import { afterAll, afterEach, @@ -9,7 +8,6 @@ import { it, vi, } from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { buildDynamoDBTables, deleteDynamoDBTables, @@ -71,15 +69,9 @@ import { writePlatformClientEntry, writeTokenGenStatesApiClient, } from "../src/utils.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests V2 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 25ef23b86b..6b8a1f1ac2 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; import { buildDynamoDBTables, deleteDynamoDBTables, @@ -44,6 +43,7 @@ import { makePlatformStatesPurposePK, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementGSIAgreement, PlatformStatesAgreementEntry, PlatformStatesCatalogEntry, PlatformStatesClientEntry, @@ -56,6 +56,7 @@ import { TokenGenerationStatesConsumerClient, TokenGenerationStatesGenericClient, unsafeBrandId, + TokenGenStatesConsumerClientGSIClient, } from "pagopa-interop-models"; import { afterAll, @@ -67,7 +68,6 @@ import { it, vi, } from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { z } from "zod"; import { setClientPurposeIdsInPlatformStatesEntry, @@ -88,15 +88,9 @@ import { upsertTokenGenStatesConsumerClient, upsertTokenGenStatesApiClient, } from "../src/utils.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("utils", () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -218,7 +212,7 @@ describe("utils", () => { ); await deleteClientEntryFromTokenGenerationStates( - clientEntry, + clientEntry.PK, dynamoDBClient ); @@ -242,7 +236,7 @@ describe("utils", () => { ); await deleteClientEntryFromTokenGenerationStates( - tokenGenStatesConsumerClient, + tokenGenStatesConsumerClient.PK, dynamoDBClient ); @@ -441,7 +435,9 @@ describe("utils", () => { dynamoDBClient ); - expect(res).toEqual(agreementEntry2); + expect(res).toEqual( + PlatformStatesAgreementGSIAgreement.parse(agreementEntry2) + ); }); describe("upsertTokenGenStatesConsumerClient", () => { @@ -521,8 +517,12 @@ describe("utils", () => { expect(res).toEqual( expect.arrayContaining([ - tokenGenStatesConsumerClientWithoutPurpose, - tokenGenStatesConsumerClientWithPurpose, + TokenGenStatesConsumerClientGSIClient.parse( + tokenGenStatesConsumerClientWithoutPurpose + ), + TokenGenStatesConsumerClientGSIClient.parse( + tokenGenStatesConsumerClientWithPurpose + ), ]) ); }); @@ -616,7 +616,7 @@ describe("utils", () => { expect(res).toEqual({ purposeEntry, - agreementEntry, + agreementEntry: PlatformStatesAgreementGSIAgreement.parse(agreementEntry), catalogEntry, }); }); diff --git a/packages/authorization-platformstate-writer/test/utils.ts b/packages/authorization-platformstate-writer/test/utils.ts index aca07c9cc6..b445e6374b 100644 --- a/packages/authorization-platformstate-writer/test/utils.ts +++ b/packages/authorization-platformstate-writer/test/utils.ts @@ -1,3 +1,11 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { inject } from "vitest"; -export const config = inject("tokenGenerationReadModelConfig"); +const config = inject("tokenGenerationReadModelConfig"); + +if (!config) { + throw new Error("config is not defined"); +} +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, +}); diff --git a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts index ec49766192..1504278003 100644 --- a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts +++ b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts @@ -19,7 +19,7 @@ import { authorizationServerErrorMapper } from "../utilities/errorMappers.js"; import { tokenServiceBuilder } from "../services/tokenService.js"; import { config } from "../config/config.js"; -const dynamoDBClient = new DynamoDBClient({}); +const dynamoDBClient = new DynamoDBClient(); const redisRateLimiter = await initRedisRateLimiter({ limiterGroup: "AUTHSERVER", maxRequests: config.rateLimiterMaxRequests, diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index 0412ded349..d291c57c80 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -7,7 +7,7 @@ import { ItemState, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, - TokenGenerationStatesConsumerClient, + TokenGenStatesConsumerClientGSIDescriptor, } from "pagopa-interop-models"; import { AttributeValue, @@ -84,7 +84,7 @@ export const readCatalogEntry = async ( if (!catalogEntry.success) { throw genericInternalError( - `Unable to parse catalog entry item: result ${JSON.stringify( + `Unable to parse platform-states catalog entry: result ${JSON.stringify( catalogEntry )} - data ${JSON.stringify(data)} ` ); @@ -185,12 +185,12 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, descriptorState: ItemState, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Descriptor", @@ -205,18 +205,20 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIDescriptor) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -256,12 +258,12 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( descriptorVoucherLifespan: number, descriptorAudience: string[], dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, IndexName: "Descriptor", @@ -276,18 +278,20 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIDescriptor) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -348,18 +352,20 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIDescriptor) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -387,7 +393,7 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = const updateDescriptorStateInTokenGenerationStatesEntries = async ( descriptorState: ItemState, dynamoDBClient: DynamoDBClient, - entriesToUpdate: TokenGenerationStatesConsumerClient[] + entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[] ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -426,7 +432,7 @@ const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ descriptorVoucherLifespan: number; descriptorAudience: string[]; dynamoDBClient: DynamoDBClient; - entriesToUpdate: TokenGenerationStatesConsumerClient[]; + entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[]; }): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -465,7 +471,7 @@ const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( voucherLifespan: number, dynamoDBClient: DynamoDBClient, - entriesToUpdate: TokenGenerationStatesConsumerClient[] + entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[] ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts index f72ee94c0c..4cdee805f0 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; import { afterAll, afterEach, @@ -32,20 +31,14 @@ import { getMockTokenGenStatesConsumerClient, buildDynamoDBTables, deleteDynamoDBTables, - readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId, + readAllTokenGenStatesItems, } from "pagopa-interop-commons-test"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { writeTokenGenStatesConsumerClient } from "pagopa-interop-commons-test"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; + describe("V1 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -152,11 +145,9 @@ describe("V1 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -272,11 +263,9 @@ describe("V1 events", async () => { expect(retrievedCatalogEntry).toEqual(expectedCatalogEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -388,11 +377,9 @@ describe("V1 events", async () => { expect(retrievedCatalogEntry).toEqual(previousCatalogStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ tokenGenStatesConsumerClient1, @@ -499,10 +486,7 @@ describe("V1 events", async () => { // token-generation-states const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + await readAllTokenGenStatesItems(dynamoDBClient); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -615,10 +599,7 @@ describe("V1 events", async () => { // token-generation-states const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + await readAllTokenGenStatesItems(dynamoDBClient); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ tokenGenStatesConsumerClient1, @@ -760,11 +741,9 @@ describe("V1 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts index 93ce8f9ca2..ae9441daa5 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { fail } from "assert"; import { afterAll, afterEach, @@ -29,7 +28,6 @@ import { makeTokenGenerationStatesClientKidPurposePK, toEServiceV2, } from "pagopa-interop-models"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { getMockDescriptor, getMockEService, @@ -37,20 +35,14 @@ import { getMockTokenGenStatesConsumerClient, buildDynamoDBTables, deleteDynamoDBTables, - readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId, + readAllTokenGenStatesItems, writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests V2 events", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -158,11 +150,9 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -269,11 +259,9 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toEqual(expectedCatalogEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -379,11 +367,9 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -483,11 +469,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -600,11 +584,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -715,11 +697,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -810,11 +790,9 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -928,11 +906,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1045,11 +1021,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1153,11 +1127,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1264,11 +1236,9 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1383,11 +1353,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(previousStateEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1504,11 +1472,9 @@ describe("integration tests V2 events", async () => { expect(retrievedEntry).toEqual(expectedEntry); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1628,11 +1594,9 @@ describe("integration tests V2 events", async () => { expect(retrievedCatalogEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index e4c8d03cd8..91c5191f25 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import crypto from "crypto"; -import { fail } from "assert"; import { afterAll, afterEach, @@ -14,6 +13,7 @@ import { import { PlatformStatesCatalogEntry, TokenGenerationStatesConsumerClient, + TokenGenStatesConsumerClientGSIDescriptor, descriptorState, generateId, itemState, @@ -21,10 +21,7 @@ import { makePlatformStatesEServiceDescriptorPK, makeTokenGenerationStatesClientKidPurposePK, } from "pagopa-interop-models"; -import { - ConditionalCheckFailedException, - DynamoDBClient, -} from "@aws-sdk/client-dynamodb"; +import { ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb"; import { getMockTokenGenStatesConsumerClient, buildDynamoDBTables, @@ -33,6 +30,7 @@ import { readAllTokenGenStatesItems, writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; +import { z } from "zod"; import { deleteCatalogEntry, descriptorStateToItemState, @@ -41,15 +39,9 @@ import { updateDescriptorStateInTokenGenerationStatesTable, writeCatalogEntry, } from "../src/utils.js"; -import { config } from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("utils tests", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -294,11 +286,9 @@ describe("utils tests", async () => { eserviceId: generateId(), descriptorId: generateId(), }); - const previousTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const previousTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(previousTokenGenStatesEntries).toEqual([]); const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { @@ -311,11 +301,9 @@ describe("utils tests", async () => { tokenGenStatesConsumerClient, dynamoDBClient ); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toEqual([ tokenGenStatesConsumerClient, @@ -386,8 +374,12 @@ describe("utils tests", async () => { expect(tokenGenStatesConsumerClients).toEqual( expect.arrayContaining([ - tokenGenStatesConsumerClient1, - tokenGenStatesConsumerClient2, + TokenGenStatesConsumerClientGSIDescriptor.parse( + tokenGenStatesConsumerClient1 + ), + TokenGenStatesConsumerClientGSIDescriptor.parse( + tokenGenStatesConsumerClient2 + ), ]) ); }); @@ -435,7 +427,11 @@ describe("utils tests", async () => { expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); expect(tokenGenStatesConsumerClients).toHaveLength(tokenEntriesLength); expect(tokenGenStatesConsumerClients).toEqual( - expect.arrayContaining(writtenTokenGenStatesConsumerClients) + expect.arrayContaining( + z + .array(TokenGenStatesConsumerClientGSIDescriptor) + .parse(writtenTokenGenStatesConsumerClients) + ) ); }); }); @@ -508,11 +504,9 @@ describe("utils tests", async () => { itemState.active, dynamoDBClient ); - const retrievedTokenGenStatesEntries = - await readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId( - eserviceId_descriptorId, - dynamoDBClient - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, diff --git a/packages/catalog-platformstate-writer/test/utils.ts b/packages/catalog-platformstate-writer/test/utils.ts index aca07c9cc6..b445e6374b 100644 --- a/packages/catalog-platformstate-writer/test/utils.ts +++ b/packages/catalog-platformstate-writer/test/utils.ts @@ -1,3 +1,11 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { inject } from "vitest"; -export const config = inject("tokenGenerationReadModelConfig"); +const config = inject("tokenGenerationReadModelConfig"); + +if (!config) { + throw new Error("config is not defined"); +} +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, +}); diff --git a/packages/commons-test/src/setupDynamoDBtables.ts b/packages/commons-test/src/setupDynamoDBtables.ts index e1dcd0a793..93c2911d41 100644 --- a/packages/commons-test/src/setupDynamoDBtables.ts +++ b/packages/commons-test/src/setupDynamoDBtables.ts @@ -10,7 +10,6 @@ export const buildDynamoDBTables = async ( dynamoDBClient: DynamoDBClient ): Promise => { const platformTableDefinition: CreateTableInput = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion TableName: "platform-states", AttributeDefinitions: [ { AttributeName: "PK", AttributeType: "S" }, @@ -33,8 +32,8 @@ export const buildDynamoDBTables = async ( }, ], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + ProjectionType: "INCLUDE", + NonKeyAttributes: ["state", "agreementDescriptorId"], }, }, ], @@ -43,7 +42,6 @@ export const buildDynamoDBTables = async ( await dynamoDBClient.send(command1); const tokenGenerationTableDefinition: CreateTableInput = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion TableName: "token-generation-states", AttributeDefinitions: [ { AttributeName: "PK", AttributeType: "S" }, @@ -66,8 +64,7 @@ export const buildDynamoDBTables = async ( }, ], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + ProjectionType: "KEYS_ONLY", }, }, { @@ -79,32 +76,50 @@ export const buildDynamoDBTables = async ( }, ], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + ProjectionType: "INCLUDE", + NonKeyAttributes: [ + "agreementState", + "descriptorAudience", + "descriptorState", + "descriptorVoucherLifespan", + ], }, }, { IndexName: "Purpose", KeySchema: [{ AttributeName: "GSIPK_purposeId", KeyType: "HASH" }], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + NonKeyAttributes: [ + "agreementId", + "agreementState", + "GSIPK_eserviceId_descriptorId", + "descriptorAudience", + "descriptorState", + "descriptorVoucherLifespan", + "purposeState", + "purposeVersionId", + ], + ProjectionType: "INCLUDE", }, }, { IndexName: "Client", KeySchema: [{ AttributeName: "GSIPK_clientId", KeyType: "HASH" }], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + ProjectionType: "INCLUDE", + NonKeyAttributes: [ + "consumerId", + "clientKind", + "publicKey", + "GSIPK_kid", + ], }, }, { IndexName: "Kid", KeySchema: [{ AttributeName: "GSIPK_kid", KeyType: "HASH" }], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + ProjectionType: "KEYS_ONLY", }, }, { @@ -113,8 +128,15 @@ export const buildDynamoDBTables = async ( { AttributeName: "GSIPK_clientId_purposeId", KeyType: "HASH" }, ], Projection: { - NonKeyAttributes: [], - ProjectionType: "ALL", + ProjectionType: "INCLUDE", + NonKeyAttributes: [ + "GSIPK_clientId", + "GSIPK_kid", + "GSIPK_purposeId", + "consumerId", + "clientKind", + "publicKey", + ], }, }, ], diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 900d1d7705..c8a91c0f2f 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -10,6 +10,7 @@ import { ScanCommandOutput, ScanInput, } from "@aws-sdk/client-dynamodb"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; import { genericInternalError, GSIPKConsumerIdEServiceId, @@ -21,8 +22,9 @@ import { PlatformStatesAgreementEntry, TokenGenerationStatesGenericClient, TokenGenerationStatesApiClient, + TokenGenStatesConsumerClientGSIAgreement, + TokenGenStatesConsumerClientGSIDescriptor, } from "pagopa-interop-models"; -import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; export const writeTokenGenStatesApiClient = async ( @@ -196,7 +198,7 @@ export const readAllTokenGenStatesItems = async ( if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -239,12 +241,12 @@ export const readAllPlatformStatesItems = async ( export const readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId = async ( gsiPKEServiceIdDescriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( gsiPKEServiceIdDescriptorId: GSIPKEServiceIdDescriptorId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: "token-generation-states", IndexName: "Descriptor", @@ -265,12 +267,12 @@ export const readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIDescriptor) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -301,12 +303,12 @@ export const readTokenGenStatesEntriesByGSIPKEServiceIdDescriptorId = async ( export const readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId = async ( gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { + ): Promise => { const input: QueryInput = { TableName: "token-generation-states", IndexName: "Agreement", @@ -327,12 +329,12 @@ export const readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIAgreement) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse toke-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index 29c769a459..a222482ac0 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -77,3 +77,16 @@ export const PlatformStatesGenericEntry = PlatformStatesCatalogEntry.or( export type PlatformStatesGenericEntry = z.infer< typeof PlatformStatesGenericEntry >; + +// GSI projection types +export const PlatformStatesAgreementGSIAgreement = + PlatformStatesAgreementEntry.pick({ + PK: true, + GSIPK_consumerId_eserviceId: true, + GSISK_agreementTimestamp: true, + agreementDescriptorId: true, + state: true, + }); +export type PlatformStatesAgreementGSIAgreement = z.infer< + typeof PlatformStatesAgreementGSIAgreement +>; diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index 193146cfb1..d3663c5039 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -70,3 +70,120 @@ export const TokenGenerationStatesGenericClient = export type TokenGenerationStatesGenericClient = z.infer< typeof TokenGenerationStatesGenericClient >; + +// GSI projection types +// Agreement +export const TokenGenStatesConsumerClientGSIAgreement = + TokenGenerationStatesConsumerClient.pick({ + PK: true, + GSIPK_consumerId_eserviceId: true, + agreementState: true, + descriptorState: true, + descriptorAudience: true, + descriptorVoucherLifespan: true, + }); +export type TokenGenStatesConsumerClientGSIAgreement = z.infer< + typeof TokenGenStatesConsumerClientGSIAgreement +>; + +// Client +export const TokenGenStatesApiClientGSIClient = + TokenGenerationStatesApiClient.pick({ + PK: true, + GSIPK_clientId: true, + consumerId: true, + clientKind: true, + publicKey: true, + GSIPK_kid: true, + }); +export type TokenGenStatesApiClientGSIClient = z.infer< + typeof TokenGenStatesApiClientGSIClient +>; + +export const TokenGenStatesConsumerClientGSIClient = + TokenGenerationStatesConsumerClient.pick({ + PK: true, + GSIPK_clientId: true, + consumerId: true, + clientKind: true, + publicKey: true, + GSIPK_kid: true, + }); +export type TokenGenStatesConsumerClientGSIClient = z.infer< + typeof TokenGenStatesConsumerClientGSIClient +>; + +export const TokenGenStatesGenericClientGSIClient = + TokenGenStatesApiClientGSIClient.or(TokenGenStatesConsumerClientGSIClient); +export type TokenGenStatesGenericClientGSIClient = z.infer< + typeof TokenGenStatesGenericClientGSIClient +>; + +// ClientPurpose +export const TokenGenStatesConsumerClientGSIClientPurpose = + TokenGenerationStatesConsumerClient.pick({ + PK: true, + GSIPK_clientId_purposeId: true, + GSIPK_clientId: true, + GSIPK_kid: true, + GSIPK_purposeId: true, + consumerId: true, + clientKind: true, + publicKey: true, + }); +export type TokenGenStatesConsumerClientGSIClientPurpose = z.infer< + typeof TokenGenStatesConsumerClientGSIClientPurpose +>; + +// Descriptor +export const TokenGenStatesConsumerClientGSIDescriptor = + TokenGenerationStatesConsumerClient.pick({ + PK: true, + GSIPK_eserviceId_descriptorId: true, + }); +export type TokenGenStatesConsumerClientGSIDescriptor = z.infer< + typeof TokenGenStatesConsumerClientGSIDescriptor +>; + +// Kid +export const TokenGenStatesApiClientGSIKid = + TokenGenerationStatesApiClient.pick({ + PK: true, + GSIPK_kid: true, + }); +export type TokenGenStatesApiClientGSIKid = z.infer< + typeof TokenGenStatesApiClientGSIKid +>; + +export const TokenGenStatesConsumerClientGSIKid = + TokenGenerationStatesConsumerClient.pick({ + PK: true, + GSIPK_kid: true, + }); +export type TokenGenStatesConsumerClientGSIKid = z.infer< + typeof TokenGenStatesConsumerClientGSIKid +>; + +export const TokenGenStatesGenericClientGSIKid = + TokenGenStatesApiClientGSIKid.or(TokenGenStatesConsumerClientGSIKid); +export type TokenGenStatesGenericClientGSIKid = z.infer< + typeof TokenGenStatesGenericClientGSIKid +>; + +// Purpose +export const TokenGenStatesConsumerClientGSIPurpose = + TokenGenerationStatesConsumerClient.pick({ + PK: true, + GSIPK_purposeId: true, + agreementId: true, + agreementState: true, + GSIPK_eserviceId_descriptorId: true, + descriptorAudience: true, + descriptorState: true, + descriptorVoucherLifespan: true, + purposeState: true, + purposeVersionId: true, + }); +export type TokenGenStatesConsumerClientGSIPurpose = z.infer< + typeof TokenGenStatesConsumerClientGSIPurpose +>; diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index 4deb704fde..62d9964e95 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -24,7 +24,7 @@ import { makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, makePlatformStatesEServiceDescriptorPK, - PlatformStatesAgreementEntry, + PlatformStatesAgreementGSIAgreement, PlatformStatesAgreementPK, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, @@ -35,7 +35,7 @@ import { PurposeVersion, PurposeVersionId, purposeVersionState, - TokenGenerationStatesConsumerClient, + TokenGenStatesConsumerClientGSIPurpose, } from "pagopa-interop-models"; import { z } from "zod"; import { config } from "./config/config.js"; @@ -106,7 +106,7 @@ export const readPlatformPurposeEntry = async ( if (!purposeEntry.success) { throw genericInternalError( - `Unable to parse purpose entry item: result ${JSON.stringify( + `Unable to parse platform-states purpose entry: result ${JSON.stringify( purposeEntry )} - data ${JSON.stringify(data)} ` ); @@ -134,7 +134,7 @@ export const readTokenGenStatesEntriesByGSIPKPurposeId = async ( purposeId: PurposeId, exclusiveStartKey?: Record ): Promise<{ - tokenGenStatesEntries: TokenGenerationStatesConsumerClient[]; + tokenGenStatesEntries: TokenGenStatesConsumerClientGSIPurpose[]; lastEvaluatedKey?: Record; }> => { const input: QueryInput = { @@ -151,18 +151,20 @@ export const readTokenGenStatesEntriesByGSIPKPurposeId = async ( if (!data.Items) { throw genericInternalError( - `Unable to read token state entries: result ${JSON.stringify(data)} ` + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` ); } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenerationStatesConsumerClient) + .array(TokenGenStatesConsumerClientGSIPurpose) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to parse token state entry item: result ${JSON.stringify( + `Unable to parse token-generation-states entries: result ${JSON.stringify( tokenGenStatesEntries )} - data ${JSON.stringify(data)} ` ); @@ -278,7 +280,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = ":agreementState": { S: platformAgreementEntry.state, }, - ":GSIPK_eserviceId_descriptorId": { + ":gsiPKEServiceIdDescriptorId": { S: makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: platformAgreementEntry.agreementDescriptorId, @@ -289,7 +291,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = const agreementUpdateExpression = isAgreementMissingInTokenGenStates ? `, agreementId = :agreementId, agreementState = :agreementState, - GSIPK_eserviceId_descriptorId = :GSIPK_eserviceId_descriptorId` + GSIPK_eserviceId_descriptorId = :gsiPKEServiceIdDescriptorId` : ""; // Descriptor data from platform-states @@ -341,7 +343,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = ":newPurposeVersionId": { S: purposeVersionId, }, - ":GSIPK_consumerId_eserviceId": { + ":gsiPKConsumerIdEServiceId": { S: makeGSIPKConsumerIdEServiceId({ consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, @@ -352,7 +354,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = }, }, UpdateExpression: - "SET purposeState = :newState, purposeVersionId = :newPurposeVersionId, GSIPK_consumerId_eserviceId = :GSIPK_consumerId_eserviceId, updatedAt = :newUpdatedAt" + + "SET purposeState = :newState, purposeVersionId = :newPurposeVersionId, GSIPK_consumerId_eserviceId = :gsiPKConsumerIdEServiceId, updatedAt = :newUpdatedAt" + agreementUpdateExpression + descriptorUpdateExpression, TableName: config.tokenGenerationReadModelTableNameTokenGeneration, @@ -455,7 +457,7 @@ export const updatePurposeDataInTokenGenStatesEntries = async ({ export const readPlatformAgreementEntry = async ( dynamoDBClient: DynamoDBClient, gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId -): Promise => { +): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNamePlatform, IndexName: "Agreement", @@ -473,14 +475,14 @@ export const readPlatformAgreementEntry = async ( } else { const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const platformAgreementEntries = z - .array(PlatformStatesAgreementEntry) + .array(PlatformStatesAgreementGSIAgreement) .safeParse(unmarshalledItems); if (platformAgreementEntries.success) { return platformAgreementEntries.data[0]; } else { throw genericInternalError( - `Unable to parse platform agreement entries: result ${JSON.stringify( + `Unable to parse platform-states agreement entries: result ${JSON.stringify( platformAgreementEntries )} ` ); @@ -509,7 +511,7 @@ export const readCatalogEntry = async ( if (!catalogEntry.success) { throw genericInternalError( - `Unable to parse catalog entry item: result ${JSON.stringify( + `Unable to parse platform-states catalog entry: result ${JSON.stringify( catalogEntry )} - data ${JSON.stringify(data)} ` ); diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts index 87e2e7ba8d..238fceb4f0 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -1,4 +1,3 @@ -import { fail } from "assert"; import { afterAll, afterEach, @@ -9,7 +8,6 @@ import { it, vi, } from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { getMockAgreement, getMockDescriptor, @@ -52,20 +50,12 @@ import { writePlatformPurposeEntry, } from "../src/utils.js"; import { - config, - readAllTokenGenStatesEntriesByGSIPKPurposeId, + dynamoDBClient, writeAgreementEntry, writeCatalogEntry, } from "./utils.js"; describe("integration tests for events V1", () => { - if (!config) { - fail(); - } - - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -230,11 +220,9 @@ describe("integration tests for events V1", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -332,11 +320,9 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -457,11 +443,9 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -587,11 +571,9 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -726,11 +708,9 @@ describe("integration tests for events V1", () => { await handleMessageV1(message, dynamoDBClient); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, @@ -865,11 +845,9 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -976,11 +954,9 @@ describe("integration tests for events V1", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1096,11 +1072,9 @@ describe("integration tests for events V1", () => { expect(retrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -1211,11 +1185,9 @@ describe("integration tests for events V1", () => { expect(retrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts index 62c1f11b52..4e6a714918 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -1,4 +1,3 @@ -import { fail } from "assert"; import { afterAll, afterEach, @@ -9,7 +8,6 @@ import { it, vi, } from "vitest"; -import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { buildDynamoDBTables, deleteDynamoDBTables, @@ -55,19 +53,12 @@ import { writePlatformPurposeEntry, } from "../src/utils.js"; import { - config, writeAgreementEntry, writeCatalogEntry, - readAllTokenGenStatesEntriesByGSIPKPurposeId, + dynamoDBClient, } from "./utils.js"; describe("integration tests for events V2", () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -231,11 +222,9 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -333,11 +322,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -436,11 +423,9 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -579,11 +564,9 @@ describe("integration tests for events V2", () => { await handleMessageV2(message, dynamoDBClient); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, @@ -722,11 +705,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -842,11 +823,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1024,11 +1003,9 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1127,11 +1104,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -1231,11 +1206,9 @@ describe("integration tests for events V2", () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1375,11 +1348,9 @@ describe("integration tests for events V2", () => { await handleMessageV2(message, dynamoDBClient); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, @@ -1518,11 +1489,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -1631,11 +1600,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1762,11 +1729,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1883,11 +1848,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -1996,11 +1959,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2127,11 +2088,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2250,11 +2209,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -2365,11 +2322,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2497,11 +2452,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2620,11 +2573,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( expect.arrayContaining([ @@ -2735,11 +2686,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2867,11 +2816,9 @@ describe("integration tests for events V2", () => { ); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -2997,11 +2944,9 @@ describe("integration tests for events V2", () => { expect(retrievedPlatformPurposeEntry).toBeUndefined(); // token-generation-states - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, diff --git a/packages/purpose-platformstate-writer/test/utils.test.ts b/packages/purpose-platformstate-writer/test/utils.test.ts index bed8a4dad3..491d147702 100644 --- a/packages/purpose-platformstate-writer/test/utils.test.ts +++ b/packages/purpose-platformstate-writer/test/utils.test.ts @@ -1,6 +1,5 @@ /* eslint-disable functional/no-let */ import crypto from "crypto"; -import { fail } from "assert"; import { afterAll, afterEach, @@ -29,11 +28,10 @@ import { makePlatformStatesPurposePK, makeTokenGenerationStatesClientKidPurposePK, purposeVersionState, + TokenGenStatesConsumerClientGSIPurpose, + PlatformStatesAgreementGSIAgreement, } from "pagopa-interop-models"; -import { - ConditionalCheckFailedException, - DynamoDBClient, -} from "@aws-sdk/client-dynamodb"; +import { ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb"; import { buildDynamoDBTables, deleteDynamoDBTables, @@ -57,19 +55,12 @@ import { writePlatformPurposeEntry, } from "../src/utils.js"; import { - config, writeAgreementEntry, writeCatalogEntry, - readAllTokenGenStatesEntriesByGSIPKPurposeId, + dynamoDBClient, } from "./utils.js"; describe("utils tests", async () => { - if (!config) { - fail(); - } - const dynamoDBClient = new DynamoDBClient({ - endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, - }); beforeEach(async () => { await buildDynamoDBTables(dynamoDBClient); }); @@ -277,8 +268,12 @@ describe("utils tests", async () => { expect(result.tokenGenStatesEntries).toEqual( expect.arrayContaining([ - tokenGenStatesConsumerClient1, - tokenGenStatesConsumerClient2, + TokenGenStatesConsumerClientGSIPurpose.parse( + tokenGenStatesConsumerClient1 + ), + TokenGenStatesConsumerClientGSIPurpose.parse( + tokenGenStatesConsumerClient2 + ), ]) ); expect(result.lastEvaluatedKey).toBeUndefined(); @@ -322,113 +317,7 @@ describe("utils tests", async () => { }); }); - describe("readAllTokenGenStatesEntriesByGSIPKPurposeId", async () => { - it("should return empty array if entries do not exist", async () => { - const purposeId: PurposeId = generateId(); - const tokenGenStatesConsumerClients = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); - expect(tokenGenStatesConsumerClients).toEqual([]); - }); - - it("should return entries if they exist (no need for pagination)", async () => { - const purposeId = generateId(); - const tokenGenStatesEntryPK1 = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = - { - ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - }; - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient1, - dynamoDBClient - ); - - const tokenGenStatesEntryPK2 = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = - { - ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - }; - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient2, - dynamoDBClient - ); - - const tokenGenStatesConsumerClients = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); - - expect(tokenGenStatesConsumerClients).toEqual( - expect.arrayContaining([ - tokenGenStatesConsumerClient1, - tokenGenStatesConsumerClient2, - ]) - ); - }); - - it("should return all entries if they exist (with pagination)", async () => { - const purposeId = generateId(); - const tokenEntriesLength = 10; - - const writtenTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = - []; - for (let i = 0; i < tokenEntriesLength; i++) { - const tokenGenStatesEntryPK = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId, - }); - const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = - { - ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), - GSIPK_purposeId: purposeId, - purposeState: itemState.inactive, - purposeVersionId: generateId(), - publicKey: crypto.randomBytes(100000).toString("hex"), - }; - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient, - dynamoDBClient - ); - // eslint-disable-next-line functional/immutable-data - writtenTokenGenStatesConsumerClients.push(tokenGenStatesConsumerClient); - } - vi.spyOn(dynamoDBClient, "send"); - const tokenGenStatesConsumerClients = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); - - expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); - expect(tokenGenStatesConsumerClients).toHaveLength(tokenEntriesLength); - expect(tokenGenStatesConsumerClients).toEqual( - expect.arrayContaining(writtenTokenGenStatesConsumerClients) - ); - }); - }); - - describe("readPlatformAgreementEntryByGSIPKConsumerIdEServiceId", async () => { + describe("readPlatformAgreementEntry", async () => { it("should return undefined if entry doesn't exist", async () => { const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: generateId(), @@ -441,7 +330,7 @@ describe("utils tests", async () => { expect(platformAgreementEntry).toBeUndefined(); }); - it("should return entry if it exists", async () => { + it("should return the most recent platform-states entry if it exists", async () => { const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: generateId(), eserviceId: generateId(), @@ -482,7 +371,9 @@ describe("utils tests", async () => { ); expect(retrievedPlatformAgreementEntry).toEqual( - previousPlatformAgreementEntry2 + PlatformStatesAgreementGSIAgreement.parse( + previousPlatformAgreementEntry2 + ) ); }); }); @@ -672,11 +563,9 @@ describe("utils tests", async () => { purposeState: itemState.active, purposeVersionId: newPurposeVersionId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purpose.id - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -796,11 +685,9 @@ describe("utils tests", async () => { consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { @@ -808,7 +695,6 @@ describe("utils tests", async () => { GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, - updatedAt: new Date().toISOString(), }; const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { @@ -816,7 +702,6 @@ describe("utils tests", async () => { GSIPK_consumerId_eserviceId, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, - updatedAt: new Date().toISOString(), }; expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( @@ -911,11 +796,9 @@ describe("utils tests", async () => { eserviceId: purpose.eserviceId, descriptorId: mockDescriptor.id, }); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, @@ -1048,11 +931,9 @@ describe("utils tests", async () => { newPurposeVersionId ); - const retrievedTokenGenStatesEntries = - await readAllTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId - ); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); const gsiPKEserviceIdDescriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: purpose.eserviceId, @@ -1071,7 +952,6 @@ describe("utils tests", async () => { descriptorAudience: previousDescriptorEntry.descriptorAudience, descriptorVoucherLifespan: previousDescriptorEntry.descriptorVoucherLifespan, - updatedAt: new Date().toISOString(), }; const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { @@ -1086,7 +966,6 @@ describe("utils tests", async () => { descriptorAudience: previousDescriptorEntry.descriptorAudience, descriptorVoucherLifespan: previousDescriptorEntry.descriptorVoucherLifespan, - updatedAt: new Date().toISOString(), }; expect(retrievedTokenGenStatesEntries).toHaveLength(2); expect(retrievedTokenGenStatesEntries).toEqual( diff --git a/packages/purpose-platformstate-writer/test/utils.ts b/packages/purpose-platformstate-writer/test/utils.ts index eab500ebda..a1c6c333d2 100644 --- a/packages/purpose-platformstate-writer/test/utils.ts +++ b/packages/purpose-platformstate-writer/test/utils.ts @@ -1,6 +1,5 @@ import { fail } from "assert"; import { - AttributeValue, DynamoDBClient, GetItemCommand, GetItemCommandOutput, @@ -12,14 +11,18 @@ import { genericInternalError, PlatformStatesAgreementEntry, PlatformStatesCatalogEntry, - PurposeId, - TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; import { inject } from "vitest"; import { unmarshall } from "@aws-sdk/util-dynamodb"; -import { readTokenGenStatesEntriesByGSIPKPurposeId } from "../src/utils.js"; -export const config = inject("tokenGenerationReadModelConfig"); +const config = inject("tokenGenerationReadModelConfig"); + +if (!config) { + throw new Error("config is not defined"); +} +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, +}); export const writeAgreementEntry = async ( agreementEntry: PlatformStatesAgreementEntry, @@ -84,7 +87,7 @@ export const readAgreementEntry = async ( if (!agreementEntry.success) { throw genericInternalError( - `Unable to parse agreement entry item: result ${JSON.stringify( + `Unable to parse platform-states agreement entry: result ${JSON.stringify( agreementEntry )} - data ${JSON.stringify(data)} ` ); @@ -130,34 +133,3 @@ export const writeCatalogEntry = async ( const command = new PutItemCommand(input); await dynamoDBClient.send(command); }; - -export const readAllTokenGenStatesEntriesByGSIPKPurposeId = async ( - dynamoDBClient: DynamoDBClient, - purposeId: PurposeId -): Promise => { - const runPaginatedQuery = async ( - dynamoDBClient: DynamoDBClient, - purposeId: PurposeId, - exclusiveStartKey?: Record - ): Promise => { - const result = await readTokenGenStatesEntriesByGSIPKPurposeId( - dynamoDBClient, - purposeId, - exclusiveStartKey - ); - if (!result.lastEvaluatedKey) { - return result.tokenGenStatesEntries; - } else { - return [ - ...result.tokenGenStatesEntries, - ...(await runPaginatedQuery( - dynamoDBClient, - purposeId, - result.lastEvaluatedKey - )), - ]; - } - }; - - return await runPaginatedQuery(dynamoDBClient, purposeId); -}; From 6707677059aa49eeac055efc26a6850e832d9506 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 6 Dec 2024 10:59:36 +0100 Subject: [PATCH 067/126] Fix delegated e-service visibility (#1258) --- .../src/services/catalogService.ts | 53 +++++++++---------- .../src/services/readModelService.ts | 8 +-- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 4fb707964a..6c47cec719 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -1887,49 +1887,44 @@ export function catalogServiceBuilder( }; } -async function isUserAllowedToSeeDraft( +function isRequesterEServiceProducer( eservice: EService, - authData: AuthData, - readModelService: ReadModelService -): Promise { - if ( - !hasPermission( + authData: AuthData +): boolean { + return ( + hasPermission( [userRoles.ADMIN_ROLE, userRoles.API_ROLE, userRoles.SUPPORT_ROLE], authData - ) - ) { - return false; - } + ) && authData.organizationId === eservice.producerId + ); +} - if (authData.organizationId === eservice.producerId) { - return true; +async function applyVisibilityToEService( + eservice: EService, + authData: AuthData, + readModelService: ReadModelService +): Promise { + if (isRequesterEServiceProducer(eservice, authData)) { + return eservice; } - const activeProducerDelegation = await readModelService.getLatestDelegation({ + const producerDelegation = await readModelService.getLatestDelegation({ eserviceId: eservice.id, delegateId: authData.organizationId, kind: delegationKind.delegatedProducer, - states: [delegationState.active], }); - return activeProducerDelegation !== undefined; -} - -async function applyVisibilityToEService( - eservice: EService, - authData: AuthData, - readModelService: ReadModelService -): Promise { - if (await isUserAllowedToSeeDraft(eservice, authData, readModelService)) { + if (producerDelegation?.state === delegationState.active) { return eservice; } - if ( - eservice.descriptors.length === 0 || - (eservice.descriptors.length === 1 && - (eservice.descriptors[0].state === descriptorState.draft || - eservice.descriptors[0].state === descriptorState.waitingForApproval)) - ) { + const hasNoActiveDescriptor = eservice.descriptors.every( + (descriptor) => + descriptor.state === descriptorState.draft || + descriptor.state === delegationState.waitingForApproval + ); + + if (!producerDelegation && hasNoActiveDescriptor) { throw eServiceNotFound(eservice.id); } diff --git a/packages/catalog-process/src/services/readModelService.ts b/packages/catalog-process/src/services/readModelService.ts index 73bb0212d5..133143a7f9 100644 --- a/packages/catalog-process/src/services/readModelService.ts +++ b/packages/catalog-process/src/services/readModelService.ts @@ -565,20 +565,22 @@ export function readModelServiceBuilder( async getLatestDelegation({ eserviceId, - states, kind, + states, delegateId, }: { eserviceId: EServiceId; - states: DelegationState[]; kind: DelegationKind; + states?: DelegationState[]; delegateId?: TenantId; }): Promise { const data = await delegations.findOne( { "data.eserviceId": eserviceId, "data.kind": kind, - ...(states.length > 0 ? { "data.state": { $in: states } } : {}), + ...(states && states.length > 0 + ? { "data.state": { $in: states } } + : {}), ...(delegateId ? { "data.delegateId": delegateId } : {}), }, { From 66b2b0113d14e0e88bcb06a974e6ecfa25d33165 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Mon, 9 Dec 2024 11:23:50 +0100 Subject: [PATCH 068/126] Fix delegated eservice agreement activation from suspension (#1266) --- packages/agreement-process/src/services/agreementService.ts | 3 ++- .../agreement-process/src/services/agreementStateProcessor.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/agreement-process/src/services/agreementService.ts b/packages/agreement-process/src/services/agreementService.ts index d18e06c920..489566295f 100644 --- a/packages/agreement-process/src/services/agreementService.ts +++ b/packages/agreement-process/src/services/agreementService.ts @@ -1054,7 +1054,8 @@ export function agreementServiceBuilder( const suspendedByProducer = suspendedByProducerFlag( agreement.data, authData.organizationId, - targetDestinationState + targetDestinationState, + delegateProducerId ); const newState = agreementStateByFlags( diff --git a/packages/agreement-process/src/services/agreementStateProcessor.ts b/packages/agreement-process/src/services/agreementStateProcessor.ts index 07d43ad7e2..1ed963afbd 100644 --- a/packages/agreement-process/src/services/agreementStateProcessor.ts +++ b/packages/agreement-process/src/services/agreementStateProcessor.ts @@ -198,7 +198,7 @@ export const suspendedByProducerFlag = ( agreement: Agreement, requesterOrgId: Tenant["id"], targetDestinationState: AgreementState, - delegateProducerId?: TenantId | undefined + delegateProducerId: TenantId | undefined ): boolean | undefined => requesterOrgId === agreement.producerId || requesterOrgId === delegateProducerId From 5aa307c9ada36af799c77c7e457212fec001a534 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Mon, 9 Dec 2024 14:52:24 +0100 Subject: [PATCH 069/126] Minor refactor on verifyVerifiedAttribute (#1262) --- .../src/routers/TenantRouter.ts | 4 +- .../src/services/readModelService.ts | 55 +++++++------ .../src/services/tenantService.ts | 81 ++++++------------- .../test/verifyVerifiedAttribute.test.ts | 41 +++++----- 4 files changed, 78 insertions(+), 103 deletions(-) diff --git a/packages/tenant-process/src/routers/TenantRouter.ts b/packages/tenant-process/src/routers/TenantRouter.ts index c400741088..26c732df27 100644 --- a/packages/tenant-process/src/routers/TenantRouter.ts +++ b/packages/tenant-process/src/routers/TenantRouter.ts @@ -826,7 +826,9 @@ const tenantsRouter = ( const tenant = await tenantService.verifyVerifiedAttribute( { tenantId: unsafeBrandId(req.params.tenantId), - tenantAttributeSeed: req.body, + attributeId: unsafeBrandId(req.body.id), + agreementId: unsafeBrandId(req.body.agreementId), + expirationDate: req.body.expirationDate, organizationId: req.ctx.authData.organizationId, correlationId: req.ctx.correlationId, }, diff --git a/packages/tenant-process/src/services/readModelService.ts b/packages/tenant-process/src/services/readModelService.ts index 19caae5843..ba57eacd94 100644 --- a/packages/tenant-process/src/services/readModelService.ts +++ b/packages/tenant-process/src/services/readModelService.ts @@ -1,5 +1,6 @@ import { AttributeCollection, + DelegationCollection, ReadModelRepository, TenantCollection, } from "pagopa-interop-commons"; @@ -22,8 +23,10 @@ import { genericInternalError, TenantFeatureType, AgreementId, - DelegationKind, Delegation, + DelegationReadModel, + delegationKind, + delegationState, } from "pagopa-interop-models"; import { tenantApi } from "pagopa-interop-api-clients"; import { z } from "zod"; @@ -156,6 +159,27 @@ async function getTenant( } } +async function getDelegation( + delegations: DelegationCollection, + filter: Filter<{ data: DelegationReadModel }> +): Promise { + const data = await delegations.findOne(filter, { + projection: { data: true }, + }); + if (data) { + const result = Delegation.safeParse(data.data); + if (!result.success) { + throw genericInternalError( + `Unable to parse delegation item: result ${JSON.stringify( + result + )} - data ${JSON.stringify(data)} ` + ); + } + return result.data; + } + return undefined; +} + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function readModelServiceBuilder( readModelRepository: ReadModelRepository @@ -533,31 +557,14 @@ export function readModelServiceBuilder( }); }, - async getActiveDelegation({ - eserviceId, - kind, - }: { - eserviceId: EServiceId; - kind: DelegationKind; - }): Promise { - const data = await delegations.findOne({ + async getActiveProducerDelegationByEservice( + eserviceId: EServiceId + ): Promise { + return getDelegation(delegations, { "data.eserviceId": eserviceId, - "data.kind": kind, - "data.state": agreementState.active, + "data.kind": delegationKind.delegatedProducer, + "data.state": delegationState.active, }); - - if (data) { - const result = Delegation.safeParse(data.data); - if (!result.success) { - throw genericInternalError( - `Unable to parse delegation item: result ${JSON.stringify( - result - )} - data ${JSON.stringify(data)} ` - ); - } - return result.data; - } - return undefined; }, }; } diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index 3536a59928..c5cea6f443 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -35,11 +35,7 @@ import { TenantFeatureType, AgreementId, Agreement, - delegationKind, - Delegation, - DelegationKind, AgreementState, - EServiceId, DelegationId, } from "pagopa-interop-models"; import { ExternalId } from "pagopa-interop-models"; @@ -175,22 +171,6 @@ async function retrieveAgreement( return agreement; } -async function retrieveActiveDelegation( - { - eserviceId, - kind, - }: { - eserviceId: EServiceId; - kind: DelegationKind; - }, - readModelService: ReadModelService -): Promise { - return await readModelService.getActiveDelegation({ - eserviceId, - kind, - }); -} - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function tenantServiceBuilder( dbInstance: DB, @@ -700,24 +680,23 @@ export function tenantServiceBuilder( async verifyVerifiedAttribute( { tenantId, - tenantAttributeSeed, + attributeId, + agreementId, + expirationDate, organizationId, correlationId, }: { tenantId: TenantId; - tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed; + attributeId: AttributeId; + agreementId: AgreementId; + expirationDate?: string; organizationId: TenantId; correlationId: CorrelationId; }, logger: Logger ): Promise { logger.info( - `Verifying attribute ${tenantAttributeSeed.id} to tenant ${tenantId} for agreement ${tenantAttributeSeed.agreementId}` - ); - - const attributeId = unsafeBrandId(tenantAttributeSeed.id); - const agreementId = unsafeBrandId( - tenantAttributeSeed.agreementId + `Verifying attribute ${attributeId} to tenant ${tenantId} for agreement ${agreementId}` ); const agreement = await retrieveAgreement(agreementId, readModelService); @@ -733,13 +712,10 @@ export function tenantServiceBuilder( throw error; } - const producerDelegation = await retrieveActiveDelegation( - { - eserviceId: agreement.eserviceId, - kind: delegationKind.delegatedProducer, - }, - readModelService - ); + const producerDelegation = + await readModelService.getActiveProducerDelegationByEservice( + agreement.eserviceId + ); const delegateProducerId = producerDelegation?.delegateId; const producerDelegator = producerDelegation?.delegatorId; @@ -775,13 +751,14 @@ export function tenantServiceBuilder( verifiedTenantAttribute, producerDelegator ?? organizationId, producerDelegation?.id, - tenantAttributeSeed + expirationDate ) : assignVerifiedAttribute( targetTenant.data.attributes, producerDelegator ?? organizationId, producerDelegation?.id, - tenantAttributeSeed + attributeId, + expirationDate ), updatedAt: new Date(), @@ -791,7 +768,7 @@ export function tenantServiceBuilder( toCreateEventTenantVerifiedAttributeAssigned( targetTenant.metadata.version, updatedTenant, - unsafeBrandId(tenantAttributeSeed.id), + attributeId, correlationId ) ); @@ -832,13 +809,10 @@ export function tenantServiceBuilder( throw error; } - const producerDelegation = await retrieveActiveDelegation( - { - eserviceId: agreement.eserviceId, - kind: delegationKind.delegatedProducer, - }, - readModelService - ); + const producerDelegation = + await readModelService.getActiveProducerDelegationByEservice( + agreement.eserviceId + ); const delegateProducerId = producerDelegation?.delegateId; const producerDelegator = producerDelegation?.delegatorId; @@ -1905,12 +1879,13 @@ function assignVerifiedAttribute( attributes: TenantAttribute[], organizationId: TenantId, producerDelegationId: DelegationId | undefined, - tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed + attributeId: AttributeId, + expirationDate: string | undefined ): TenantAttribute[] { return [ ...attributes, { - id: unsafeBrandId(tenantAttributeSeed.id), + id: attributeId, type: tenantAttributeType.VERIFIED, assignmentTimestamp: new Date(), verifiedBy: [ @@ -1918,12 +1893,8 @@ function assignVerifiedAttribute( id: organizationId, delegationId: producerDelegationId, verificationDate: new Date(), - expirationDate: tenantAttributeSeed.expirationDate - ? new Date(tenantAttributeSeed.expirationDate) - : undefined, - extensionDate: tenantAttributeSeed.expirationDate - ? new Date(tenantAttributeSeed.expirationDate) - : undefined, + expirationDate: expirationDate ? new Date(expirationDate) : undefined, + extensionDate: expirationDate ? new Date(expirationDate) : undefined, }, ], revokedBy: [], @@ -1936,7 +1907,7 @@ function reassignVerifiedAttribute( verifiedTenantAttribute: VerifiedTenantAttribute, organizationId: TenantId, producerDelegationId: DelegationId | undefined, - tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed + expirationDate: string | undefined ): TenantAttribute[] { return attributes.map((attr) => attr.id === verifiedTenantAttribute.id @@ -1946,7 +1917,7 @@ function reassignVerifiedAttribute( verifiedTenantAttribute.verifiedBy, organizationId, producerDelegationId, - tenantAttributeSeed.expirationDate + expirationDate ), revokedBy: verifiedTenantAttribute.revokedBy.filter( (i) => i.id !== organizationId diff --git a/packages/tenant-process/test/verifyVerifiedAttribute.test.ts b/packages/tenant-process/test/verifyVerifiedAttribute.test.ts index 59dc8efb4a..73156b35ba 100644 --- a/packages/tenant-process/test/verifyVerifiedAttribute.test.ts +++ b/packages/tenant-process/test/verifyVerifiedAttribute.test.ts @@ -15,6 +15,7 @@ import { Agreement, delegationState, delegationKind, + AttributeId, } from "pagopa-interop-models"; import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; @@ -26,7 +27,6 @@ import { getMockTenant, getMockDelegation, } from "pagopa-interop-commons-test"; -import { tenantApi } from "pagopa-interop-api-clients"; import { tenantNotFound, attributeVerificationNotAllowed, @@ -51,7 +51,7 @@ describe("verifyVerifiedAttribute", async () => { const targetTenant = getMockTenant(); const requesterTenant = getMockTenant(); - const tenantAttributeSeedId = generateId(); + const tenantAttributeSeedId = generateId(); const attribute: Attribute = { ...getMockAttribute(), @@ -85,10 +85,6 @@ describe("verifyVerifiedAttribute", async () => { producerId: eService1.producerId, consumerId: targetTenant.id, }); - const tenantAttributeSeed: tenantApi.VerifiedTenantAttributeSeed = { - id: tenantAttributeSeedId, - agreementId: agreementEservice1.id, - }; const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, @@ -130,7 +126,8 @@ describe("verifyVerifiedAttribute", async () => { const returnedTenant = await tenantService.verifyVerifiedAttribute( { tenantId: targetTenant.id, - tenantAttributeSeed, + attributeId: tenantAttributeSeedId, + agreementId: agreementEservice1.id, organizationId: requesterTenant.id, correlationId: generateId(), }, @@ -157,7 +154,7 @@ describe("verifyVerifiedAttribute", async () => { ...targetTenant, attributes: [ { - id: unsafeBrandId(tenantAttributeSeed.id), + id: tenantAttributeSeedId, type: tenantAttributeType.VERIFIED, assignmentTimestamp: new Date(), verifiedBy: [ @@ -165,12 +162,8 @@ describe("verifyVerifiedAttribute", async () => { id: hasDelegation ? delegation.delegatorId : requesterTenant.id, delegationId: hasDelegation ? delegation.id : undefined, verificationDate: new Date(), - expirationDate: tenantAttributeSeed.expirationDate - ? new Date(tenantAttributeSeed.expirationDate) - : undefined, - extensionDate: tenantAttributeSeed.expirationDate - ? new Date(tenantAttributeSeed.expirationDate) - : undefined, + expirationDate: undefined, + extensionDate: undefined, }, ], revokedBy: [], @@ -226,7 +219,8 @@ describe("verifyVerifiedAttribute", async () => { const returnedTenant = await tenantService.verifyVerifiedAttribute( { tenantId: tenantWithVerifiedAttribute.id, - tenantAttributeSeed, + attributeId: tenantAttributeSeedId, + agreementId: agreementEservice1.id, organizationId: requesterTenant.id, correlationId: generateId(), }, @@ -282,7 +276,8 @@ describe("verifyVerifiedAttribute", async () => { tenantService.verifyVerifiedAttribute( { tenantId: targetTenant.id, - tenantAttributeSeed, + attributeId: tenantAttributeSeedId, + agreementId: agreementEservice1.id, organizationId: requesterTenant.id, correlationId: generateId(), }, @@ -300,7 +295,8 @@ describe("verifyVerifiedAttribute", async () => { tenantService.verifyVerifiedAttribute( { tenantId: targetTenant.id, - tenantAttributeSeed, + attributeId: tenantAttributeSeedId, + agreementId: agreementEservice1.id, organizationId: requesterTenant.id, correlationId: generateId(), }, @@ -345,10 +341,8 @@ describe("verifyVerifiedAttribute", async () => { tenantService.verifyVerifiedAttribute( { tenantId: targetTenant.id, - tenantAttributeSeed: { - ...tenantAttributeSeed, - agreementId: agreementEserviceWithNotAllowedDescriptor.id, - }, + attributeId: tenantAttributeSeedId, + agreementId: agreementEserviceWithNotAllowedDescriptor.id, organizationId: requesterTenant.id, correlationId: generateId(), }, @@ -357,7 +351,7 @@ describe("verifyVerifiedAttribute", async () => { ).rejects.toThrowError( attributeVerificationNotAllowed( targetTenant.id, - unsafeBrandId(tenantAttributeSeed.id) + unsafeBrandId(tenantAttributeSeedId) ) ); }); @@ -371,7 +365,8 @@ describe("verifyVerifiedAttribute", async () => { tenantService.verifyVerifiedAttribute( { tenantId: targetTenant.id, - tenantAttributeSeed, + attributeId: tenantAttributeSeedId, + agreementId: agreementEservice1.id, organizationId: targetTenant.id, correlationId: generateId(), }, From 0afa109809baf5213f530f1ac708c9266d8702c8 Mon Sep 17 00:00:00 2001 From: paologaleotti <45665769+paologaleotti@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:20:10 +0100 Subject: [PATCH 070/126] PIN-5666 GET delegations by id tests (#1234) Co-authored-by: Andrea Zerbini --- .../test/getDelegationById.test.ts | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/delegation-process/test/getDelegationById.test.ts b/packages/delegation-process/test/getDelegationById.test.ts index 9a323bbaec..53afc255fe 100644 --- a/packages/delegation-process/test/getDelegationById.test.ts +++ b/packages/delegation-process/test/getDelegationById.test.ts @@ -11,36 +11,36 @@ import { delegationNotFound } from "../src/model/domain/errors.js"; import { addOneDelegation, delegationService } from "./utils.js"; describe("get delegation by id", () => { - it("should get the delegation if it exists", async () => { - const delegation = getMockDelegation({ - kind: delegationKind.delegatedConsumer, - }); - - await addOneDelegation(delegation); - - const expectedDelegation = await delegationService.getDelegationById( - delegation.id, - genericLogger - ); - - expect(delegation).toEqual(expectedDelegation); - }); - - it("should fail with delegationNotFound", async () => { - const delegation = getMockDelegation({ - kind: delegationKind.delegatedConsumer, - }); - - await addOneDelegation(delegation); - - const notFoundId = generateId(); - const expectedDelegation = delegationService.getDelegationById( - notFoundId, - genericLogger - ); - - await expect(expectedDelegation).rejects.toThrow( - delegationNotFound(notFoundId) - ); - }); + it.each(Object.values(delegationKind))( + "should get the %s delegation if it exists", + async (kind) => { + const delegation = getMockDelegation({ kind }); + await addOneDelegation(delegation); + + const expectedDelegation = await delegationService.getDelegationById( + delegation.id, + genericLogger + ); + + expect(delegation).toEqual(expectedDelegation); + } + ); + + it.each(Object.values(delegationKind))( + "should fail with delegationNotFound for %s delegations", + async (kind) => { + const delegation = getMockDelegation({ kind }); + await addOneDelegation(delegation); + + const notFoundId = generateId(); + const expectedDelegation = delegationService.getDelegationById( + notFoundId, + genericLogger + ); + + await expect(expectedDelegation).rejects.toThrow( + delegationNotFound(notFoundId) + ); + } + ); }); From e29b96aa14f614798ebff2953266940470a70ca3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 9 Dec 2024 15:57:43 +0100 Subject: [PATCH 071/126] IMN-521 Fix audience validation (#1233) Co-authored-by: shuyec <76391491+shuyec@users.noreply.github.com> --- .../authorization-server/src/config/config.ts | 2 + .../src/services/tokenService.ts | 19 +- .../authorizationServer.integration.test.ts | 28 +- .../backend-for-frontend/src/config/config.ts | 4 +- .../src/services/toolService.ts | 7 +- packages/client-assertion-validation/.env | 1 - .../client-assertion-validation/src/config.ts | 6 +- .../client-assertion-validation/src/errors.ts | 75 ++-- .../client-assertion-validation/src/index.ts | 1 + .../client-assertion-validation/src/utils.ts | 26 +- .../src/validation.ts | 7 +- .../client-assertion-validation/test/utils.ts | 1 + .../test/validation.test.ts | 388 +++++++++++++++--- .../src/interop-token/interopTokenService.ts | 22 +- packages/commons/src/interop-token/models.ts | 19 +- packages/commons/src/utils/date.ts | 8 + 16 files changed, 450 insertions(+), 164 deletions(-) delete mode 100644 packages/client-assertion-validation/.env diff --git a/packages/authorization-server/src/config/config.ts b/packages/authorization-server/src/config/config.ts index 8e81b097a3..7d525193b8 100644 --- a/packages/authorization-server/src/config/config.ts +++ b/packages/authorization-server/src/config/config.ts @@ -8,12 +8,14 @@ import { LoggerConfig, } from "pagopa-interop-commons"; import { z } from "zod"; +import { ClientAssertionValidationConfig } from "pagopa-interop-client-assertion-validation"; const AuthorizationServerConfig = HTTPServerConfig.and(LoggerConfig) .and(RedisRateLimiterConfig) .and(KafkaProducerConfig) .and(FileManagerConfig) .and(S3Config) + .and(ClientAssertionValidationConfig) .and( z .object({ diff --git a/packages/authorization-server/src/services/tokenService.ts b/packages/authorization-server/src/services/tokenService.ts index 39586c0185..88a25b18fa 100644 --- a/packages/authorization-server/src/services/tokenService.ts +++ b/packages/authorization-server/src/services/tokenService.ts @@ -43,6 +43,7 @@ import { Logger, RateLimiter, RateLimiterStatus, + secondsToMilliseconds, } from "pagopa-interop-commons"; import { initProducer } from "kafka-iam-auth"; import { config } from "../config/config.js"; @@ -102,7 +103,11 @@ export function tokenServiceBuilder({ } const { data: jwt, errors: clientAssertionErrors } = - verifyClientAssertion(request.client_assertion, request.client_id); + verifyClientAssertion( + request.client_assertion, + request.client_id, + config.clientAssertionAudience + ); if (clientAssertionErrors) { // TODO double check if errors have to be logged or put inside the error below (check the same for parameters errors) @@ -266,7 +271,7 @@ export const publishAudit = async ({ const messageBody: GeneratedTokenAuditDetails = { jwtId: generatedToken.payload.jti, correlationId, - issuedAt: generatedToken.payload.iat, + issuedAt: secondsToMilliseconds(generatedToken.payload.iat), clientId: clientAssertion.payload.sub, organizationId: key.consumerId, agreementId: key.agreementId, @@ -280,16 +285,16 @@ export const publishAudit = async ({ purposeVersionId: unsafeBrandId(key.purposeVersionId), algorithm: generatedToken.header.alg, keyId: generatedToken.header.kid, - audience: generatedToken.payload.aud.join(","), + audience: [generatedToken.payload.aud].flat().join(","), subject: generatedToken.payload.sub, - notBefore: generatedToken.payload.nbf, - expirationTime: generatedToken.payload.exp, + notBefore: secondsToMilliseconds(generatedToken.payload.nbf), + expirationTime: secondsToMilliseconds(generatedToken.payload.exp), issuer: generatedToken.payload.iss, clientAssertion: { algorithm: clientAssertion.header.alg, audience: [clientAssertion.payload.aud].flat().join(","), - expirationTime: clientAssertion.payload.exp, - issuedAt: clientAssertion.payload.iat, + expirationTime: secondsToMilliseconds(clientAssertion.payload.exp), + issuedAt: secondsToMilliseconds(clientAssertion.payload.iat), issuer: clientAssertion.payload.iss, jwtId: clientAssertion.payload.jti, keyId: clientAssertion.header.kid, diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts index 5fcbb1204b..d545f6e0d4 100644 --- a/packages/authorization-server/test/authorizationServer.integration.test.ts +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -32,7 +32,11 @@ import { TokenGenerationStatesConsumerClient, unsafeBrandId, } from "pagopa-interop-models"; -import { formatDateyyyyMMdd, genericLogger } from "pagopa-interop-commons"; +import { + formatDateyyyyMMdd, + genericLogger, + secondsToMilliseconds, +} from "pagopa-interop-commons"; import { authorizationServerApi } from "pagopa-interop-api-clients"; import { config } from "../src/config/config.js"; import { @@ -566,7 +570,7 @@ describe("authorization server tests", () => { const expectedMessageBody: GeneratedTokenAuditDetails = { jwtId: generateId(), correlationId, - issuedAt: parsedDecodedFileContent.issuedAt, + issuedAt: secondsToMilliseconds(parsedDecodedFileContent.issuedAt), clientId, organizationId: tokenClientKidPurposeEntry.consumerId, agreementId: unsafeBrandId( @@ -584,14 +588,16 @@ describe("authorization server tests", () => { keyId: config.generatedInteropTokenKid, audience: tokenClientKidPurposeEntry.descriptorAudience!.join(","), subject: clientId, - notBefore: parsedDecodedFileContent.notBefore, - expirationTime: parsedDecodedFileContent.expirationTime, + notBefore: secondsToMilliseconds(parsedDecodedFileContent.notBefore), + expirationTime: secondsToMilliseconds( + parsedDecodedFileContent.expirationTime + ), issuer: config.generatedInteropTokenIssuer, clientAssertion: { algorithm: clientAssertion.header.alg, audience: [clientAssertion.payload.aud].flat().join(","), - expirationTime: clientAssertion.payload.exp!, - issuedAt: clientAssertion.payload.iat!, + expirationTime: secondsToMilliseconds(clientAssertion.payload.exp!), + issuedAt: secondsToMilliseconds(clientAssertion.payload.iat!), issuer: clientAssertion.payload.iss!, jwtId: clientAssertion.payload.jti!, keyId: clientAssertion.header.kid!, @@ -700,7 +706,7 @@ describe("authorization server tests", () => { const expectedMessageBody: GeneratedTokenAuditDetails = { jwtId: generateId(), correlationId, - issuedAt: parsedAuditSent.issuedAt, + issuedAt: secondsToMilliseconds(parsedAuditSent.issuedAt), clientId, organizationId: tokenClientPurposeEntry.consumerId, agreementId: unsafeBrandId( @@ -718,14 +724,14 @@ describe("authorization server tests", () => { keyId: config.generatedInteropTokenKid, audience: tokenClientPurposeEntry.descriptorAudience!.join(","), subject: clientId, - notBefore: parsedAuditSent.notBefore, - expirationTime: parsedAuditSent.expirationTime, + notBefore: secondsToMilliseconds(parsedAuditSent.notBefore), + expirationTime: secondsToMilliseconds(parsedAuditSent.expirationTime), issuer: config.generatedInteropTokenIssuer, clientAssertion: { algorithm: clientAssertion.header.alg, audience: [clientAssertion.payload.aud].flat().join(","), - expirationTime: clientAssertion.payload.exp!, - issuedAt: clientAssertion.payload.iat!, + expirationTime: secondsToMilliseconds(clientAssertion.payload.exp!), + issuedAt: secondsToMilliseconds(clientAssertion.payload.iat!), issuer: clientAssertion.payload.iss!, jwtId: clientAssertion.payload.jti!, keyId: clientAssertion.header.kid!, diff --git a/packages/backend-for-frontend/src/config/config.ts b/packages/backend-for-frontend/src/config/config.ts index 6ee5e8f0bd..896795c769 100644 --- a/packages/backend-for-frontend/src/config/config.ts +++ b/packages/backend-for-frontend/src/config/config.ts @@ -8,6 +8,7 @@ import { TokenGenerationConfig, } from "pagopa-interop-commons"; import { z } from "zod"; +import { ClientAssertionValidationConfig } from "pagopa-interop-client-assertion-validation"; export const TenantProcessServerConfig = z .object({ @@ -207,7 +208,8 @@ const BffProcessConfig = CommonHTTPServiceConfig.and(TenantProcessServerConfig) .and(S3PrivacyNoticeConfig) .and(ExportFileConfig) .and(ImportFileConfig) - .and(InterfaceVersion); + .and(InterfaceVersion) + .and(ClientAssertionValidationConfig); export type BffProcessConfig = z.infer; export const config: BffProcessConfig = BffProcessConfig.parse(process.env); diff --git a/packages/backend-for-frontend/src/services/toolService.ts b/packages/backend-for-frontend/src/services/toolService.ts index 4044432683..181c2bc634 100644 --- a/packages/backend-for-frontend/src/services/toolService.ts +++ b/packages/backend-for-frontend/src/services/toolService.ts @@ -52,6 +52,7 @@ import { PagoPAInteropBeClients, AgreementProcessClient, } from "../clients/clientsProvider.js"; +import { config } from "../config/config.js"; import { getAllAgreements } from "./agreementService.js"; export function toolsServiceBuilder(clients: PagoPAInteropBeClients) { @@ -73,7 +74,11 @@ export function toolsServiceBuilder(clients: PagoPAInteropBeClients) { }); const { data: jwt, errors: clientAssertionErrors } = - verifyClientAssertion(clientAssertion, clientId); + verifyClientAssertion( + clientAssertion, + clientId, + config.clientAssertionAudience + ); if (parametersErrors || clientAssertionErrors) { return handleValidationResults({ diff --git a/packages/client-assertion-validation/.env b/packages/client-assertion-validation/.env deleted file mode 100644 index 4707096839..0000000000 --- a/packages/client-assertion-validation/.env +++ /dev/null @@ -1 +0,0 @@ -CLIENT_ASSERTION_AUDIENCE="test.interop.pagopa.it,dev.interop.pagopa.it" diff --git a/packages/client-assertion-validation/src/config.ts b/packages/client-assertion-validation/src/config.ts index 958c37ea6a..39e63600ae 100644 --- a/packages/client-assertion-validation/src/config.ts +++ b/packages/client-assertion-validation/src/config.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -const ClientAssertionValidationConfig = z +export const ClientAssertionValidationConfig = z .object({ CLIENT_ASSERTION_AUDIENCE: z.string(), }) @@ -10,4 +10,6 @@ const ClientAssertionValidationConfig = z ), })); -export const config = ClientAssertionValidationConfig.parse(process.env); +export type ClientAssertionValidationConfig = z.infer< + typeof ClientAssertionValidationConfig +>; diff --git a/packages/client-assertion-validation/src/errors.ts b/packages/client-assertion-validation/src/errors.ts index c9299d7797..bc7a8f8491 100644 --- a/packages/client-assertion-validation/src/errors.ts +++ b/packages/client-assertion-validation/src/errors.ts @@ -4,37 +4,36 @@ export const errorCodes = { unexpectedClientAssertionSignatureVerificationError: "0001", invalidAssertionType: "0002", invalidGrantType: "0003", - invalidAudienceFormat: "0004", - invalidAudience: "0005", - audienceNotFound: "0006", - invalidClientAssertionFormat: "0007", - unexpectedClientAssertionPayload: "0008", - jtiNotFound: "0009", - issuedAtNotFound: "0010", - expNotFound: "0011", - issuerNotFound: "0012", - subjectNotFound: "0013", - invalidSubject: "0014", - invalidPurposeIdClaimFormat: "0015", - kidNotFound: "0016", - clientAssertionSignatureVerificationError: "0017", - tokenExpiredError: "0018", - jsonWebTokenError: "0019", - notBeforeError: "0020", - invalidPurposeState: "0021", - invalidAgreementState: "0022", - invalidEServiceState: "0023", - invalidClientIdFormat: "0024", - invalidSubjectFormat: "0025", - digestClaimNotFound: "0026", - invalidHashLength: "0027", - invalidHashAlgorithm: "0028", - algorithmNotFound: "0029", - algorithmNotAllowed: "0030", - purposeIdNotProvided: "0031", - invalidKidFormat: "0032", - clientAssertionInvalidClaims: "0033", - invalidSignature: "0034", + invalidAudience: "0004", + audienceNotFound: "0005", + invalidClientAssertionFormat: "0006", + unexpectedClientAssertionPayload: "0007", + jtiNotFound: "0008", + issuedAtNotFound: "0009", + expNotFound: "0010", + issuerNotFound: "0011", + subjectNotFound: "0012", + invalidSubject: "0013", + invalidPurposeIdClaimFormat: "0014", + kidNotFound: "0015", + clientAssertionSignatureVerificationError: "0016", + tokenExpiredError: "0017", + jsonWebTokenError: "0018", + notBeforeError: "0019", + invalidPurposeState: "0020", + invalidAgreementState: "0021", + invalidEServiceState: "0022", + invalidClientIdFormat: "0023", + invalidSubjectFormat: "0024", + digestClaimNotFound: "0025", + invalidHashLength: "0026", + invalidHashAlgorithm: "0027", + algorithmNotFound: "0028", + algorithmNotAllowed: "0029", + purposeIdNotProvided: "0030", + invalidKidFormat: "0031", + clientAssertionInvalidClaims: "0032", + invalidSignature: "0033", }; export type ErrorCodes = keyof typeof errorCodes; @@ -77,17 +76,11 @@ export function invalidGrantType(grantType: string): ApiError { }); } -export function invalidAudienceFormat(): ApiError { - return new ApiError({ - detail: "Audience must be an array or a string in case of single value", - code: "invalidAudienceFormat", - title: "Invalid audience format", - }); -} - -export function invalidAudience(): ApiError { +export function invalidAudience( + aud: string | string[] | undefined +): ApiError { return new ApiError({ - detail: "Unexpected client assertion audience", + detail: `Unexpected client assertion audience: ${aud}`, code: "invalidAudience", title: "Invalid audience", }); diff --git a/packages/client-assertion-validation/src/index.ts b/packages/client-assertion-validation/src/index.ts index 293a920555..34e9d4ac37 100644 --- a/packages/client-assertion-validation/src/index.ts +++ b/packages/client-assertion-validation/src/index.ts @@ -1,2 +1,3 @@ export * from "./validation.js"; export * from "./types.js"; +export * from "./config.js"; diff --git a/packages/client-assertion-validation/src/utils.ts b/packages/client-assertion-validation/src/utils.ts index bc4bc4ce24..9ff951d463 100644 --- a/packages/client-assertion-validation/src/utils.ts +++ b/packages/client-assertion-validation/src/utils.ts @@ -32,12 +32,10 @@ import { invalidKidFormat, digestClaimNotFound, audienceNotFound, - invalidAudienceFormat, invalidAgreementState, invalidEServiceState, invalidPurposeState, } from "./errors.js"; -import { config } from "./config.js"; export const EXPECTED_CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; @@ -129,27 +127,23 @@ export const validateKid = (kid?: string): ValidationResult => { }; export const validateAudience = ( - aud: string | string[] | undefined + receivedAudiences: string | string[] | undefined, + expectedAudiences: string[] ): ValidationResult => { - if (!aud) { + if (!receivedAudiences) { return failedValidation([audienceNotFound()]); } - if (Array.isArray(aud)) { - if (config.clientAssertionAudience.every((entry) => aud.includes(entry))) { - return successfulValidation(aud); + if (Array.isArray(receivedAudiences)) { + if (expectedAudiences.some((entry) => receivedAudiences.includes(entry))) { + return successfulValidation(receivedAudiences); } - return failedValidation([invalidAudience()]); + return failedValidation([invalidAudience(receivedAudiences)]); } else { - const split = aud.split(",").map((s) => s.trim()); - if (split.length > 1) { - return failedValidation([invalidAudienceFormat()]); + if (expectedAudiences.some((entry) => receivedAudiences === entry)) { + return successfulValidation(receivedAudiences); } - const audEntry = split[0]; - if (config.clientAssertionAudience.every((entry) => audEntry === entry)) { - return successfulValidation(aud); - } - return failedValidation([invalidAudience()]); + return failedValidation([invalidAudience(receivedAudiences)]); } }; diff --git a/packages/client-assertion-validation/src/validation.ts b/packages/client-assertion-validation/src/validation.ts index 8cde476757..c412020488 100644 --- a/packages/client-assertion-validation/src/validation.ts +++ b/packages/client-assertion-validation/src/validation.ts @@ -74,10 +74,10 @@ export const validateRequestParameters = ( return failedValidation([assertionTypeError, grantTypeError]); }; -// eslint-disable-next-line complexity export const verifyClientAssertion = ( clientAssertionJws: string, - clientId: string | undefined + clientId: string | undefined, + expectedAudiences: string[] ): ValidationResult => { try { const decodedPayload = jose.decodeJwt(clientAssertionJws); @@ -105,7 +105,8 @@ export const verifyClientAssertion = ( decodedHeader.kid ); const { errors: audErrors, data: validatedAud } = validateAudience( - decodedPayload.aud + decodedPayload.aud, + expectedAudiences ); const { errors: algErrors, data: validatedAlg } = validateAlgorithm( decodedHeader.alg diff --git a/packages/client-assertion-validation/test/utils.ts b/packages/client-assertion-validation/test/utils.ts index 4f35335375..d3ae5aca70 100644 --- a/packages/client-assertion-validation/test/utils.ts +++ b/packages/client-assertion-validation/test/utils.ts @@ -8,6 +8,7 @@ import { } from "../src/utils.js"; export const value64chars = crypto.randomBytes(32).toString("hex"); +export const expectedAudiences = ["test.interop.pagopa.it"]; export const getMockAccessTokenRequest = async (): Promise => ({ diff --git a/packages/client-assertion-validation/test/validation.test.ts b/packages/client-assertion-validation/test/validation.test.ts index bdf1dcbdb7..54316133d1 100644 --- a/packages/client-assertion-validation/test/validation.test.ts +++ b/packages/client-assertion-validation/test/validation.test.ts @@ -24,7 +24,7 @@ import { verifyClientAssertion, verifyClientAssertionSignature, } from "../src/validation.js"; -import { validatePlatformState } from "../src/utils.js"; +import { validateAudience, validatePlatformState } from "../src/utils.js"; import { algorithmNotAllowed, algorithmNotFound, @@ -54,11 +54,15 @@ import { invalidAssertionType, invalidSignature, clientAssertionInvalidClaims, - invalidAudienceFormat, unexpectedClientAssertionSignatureVerificationError, + audienceNotFound, } from "../src/errors.js"; import { ClientAssertionValidationRequest } from "../src/types.js"; -import { getMockAccessTokenRequest, value64chars } from "./utils.js"; +import { + expectedAudiences, + getMockAccessTokenRequest, + value64chars, +} from "./utils.js"; describe("validation test", async () => { describe("validateRequestParameters", async () => { @@ -114,7 +118,11 @@ describe("validation test", async () => { describe("verifyClientAssertion", async () => { it("success client assertion", async () => { const { jws } = await getMockClientAssertion(); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeUndefined(); }); @@ -124,7 +132,11 @@ describe("validation test", async () => { invalidHeaderProp: "wrong", }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -137,7 +149,11 @@ describe("validation test", async () => { wrongPayloadProp: "wrong", }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0].code).toEqual(clientAssertionInvalidClaims("").code); @@ -149,7 +165,8 @@ describe("validation test", async () => { const clientAssertionWithWrongSignature = `${subStrings[0]}.${subStrings[1]}.wrong-signature`; const { errors } = verifyClientAssertion( clientAssertionWithWrongSignature, - undefined + undefined, + expectedAudiences ); expect(errors).toBeUndefined(); }); @@ -163,7 +180,8 @@ describe("validation test", async () => { const clientAssertionWithWrongSignature = `${subStrings1[0]}.${subStrings1[1]}.${subStrings2[2]}`; const { errors } = verifyClientAssertion( clientAssertionWithWrongSignature, - undefined + undefined, + expectedAudiences ); expect(errors).toBeUndefined(); }); @@ -171,18 +189,27 @@ describe("validation test", async () => { it("invalidClientAssertionFormat (malformed jwt)", async () => { const { errors: errors1 } = verifyClientAssertion( "too.many.substrings.in.client.assertion", - undefined + undefined, + expectedAudiences ); expect(errors1).toBeDefined(); expect(errors1).toHaveLength(1); expect(errors1![0]).toEqual(invalidClientAssertionFormat("Invalid JWT")); - const { errors: errors2 } = verifyClientAssertion("not a jwt", undefined); + const { errors: errors2 } = verifyClientAssertion( + "not a jwt", + undefined, + expectedAudiences + ); expect(errors2).toBeDefined(); expect(errors2).toHaveLength(1); expect(errors2![0]).toEqual(invalidClientAssertionFormat("Invalid JWT")); - const { errors: errors3 } = verifyClientAssertion("not.a.jwt", undefined); + const { errors: errors3 } = verifyClientAssertion( + "not.a.jwt", + undefined, + expectedAudiences + ); expect(errors3).toBeDefined(); expect(errors3).toHaveLength(1); expect(errors3![0]).toEqual( @@ -193,7 +220,8 @@ describe("validation test", async () => { const { errors: errors4 } = verifyClientAssertion( "signature.missing", - undefined + undefined, + expectedAudiences ); expect(errors4).toBeDefined(); expect(errors4).toHaveLength(1); @@ -201,55 +229,48 @@ describe("validation test", async () => { }); it("invalidAudience - wrong entry as string", async () => { + const aud = "random"; const { jws } = await getMockClientAssertion({ - standardClaimsOverride: { aud: "random" }, + standardClaimsOverride: { aud }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(invalidAudience()); + expect(errors![0]).toEqual(invalidAudience(aud)); }); it("invalidAudience - wrong entry as 1-item array", async () => { + const aud = ["random"]; const { jws } = await getMockClientAssertion({ - standardClaimsOverride: { aud: ["random"] }, + standardClaimsOverride: { aud }, }); - const { errors } = verifyClientAssertion(jws, undefined); - expect(errors).toBeDefined(); - expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(invalidAudience()); - }); - - it("invalidAudienceFormat - comma-separated strings", async () => { - const { jws } = await getMockClientAssertion({ - standardClaimsOverride: { aud: "test.interop.pagopa.it, other-aud" }, - }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(invalidAudienceFormat()); + expect(errors![0]).toEqual(invalidAudience(aud)); }); it("invalidAudience - wrong entries", async () => { + const aud = ["wrong-audience1", "wrong-audience2"]; const { jws } = await getMockClientAssertion({ - standardClaimsOverride: { aud: ["wrong-audience1, wrong-audience2"] }, - }); - const { errors } = verifyClientAssertion(jws, undefined); - expect(errors).toBeDefined(); - expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(invalidAudience()); - }); - - it("invalidAudience - missing entry", async () => { - const { jws } = await getMockClientAssertion({ - standardClaimsOverride: { - aud: ["test.interop.pagopa.it"], - }, + standardClaimsOverride: { aud }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); - expect(errors![0]).toEqual(invalidAudience()); + expect(errors![0]).toEqual(invalidAudience(aud)); }); it("unexpectedClientAssertionPayload", async () => { @@ -266,7 +287,11 @@ describe("validation test", async () => { options ); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); @@ -281,7 +306,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ standardClaimsOverride: { jti: undefined }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(jtiNotFound()); @@ -293,7 +322,11 @@ describe("validation test", async () => { iat: undefined, }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(issuedAtNotFound()); @@ -305,7 +338,11 @@ describe("validation test", async () => { exp: undefined, }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(expNotFound()); @@ -315,7 +352,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ standardClaimsOverride: { iss: undefined }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(issuerNotFound()); @@ -325,7 +366,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ standardClaimsOverride: { jti: undefined, iss: undefined }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(2); expect(errors).toEqual([jtiNotFound(), issuerNotFound()]); @@ -335,7 +380,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ standardClaimsOverride: { sub: undefined }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(subjectNotFound()); @@ -346,7 +395,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ standardClaimsOverride: { sub: subject }, }); - const { errors } = verifyClientAssertion(jws, generateId()); + const { errors } = verifyClientAssertion( + jws, + generateId(), + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidSubject(subject)); @@ -358,7 +411,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ standardClaimsOverride: { sub: subject }, }); - const { errors } = verifyClientAssertion(jws, clientId); + const { errors } = verifyClientAssertion( + jws, + clientId, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidSubjectFormat(subject)); @@ -371,7 +428,11 @@ describe("validation test", async () => { purposeId: notPurposeId, }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidPurposeIdClaimFormat(notPurposeId)); @@ -380,7 +441,11 @@ describe("validation test", async () => { it("invalidClientIdFormat", async () => { const notClientId = "not a client id"; const { jws } = await getMockClientAssertion(); - const { errors } = verifyClientAssertion(jws, notClientId); + const { errors } = verifyClientAssertion( + jws, + notClientId, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidClientIdFormat(notClientId)); @@ -393,7 +458,11 @@ describe("validation test", async () => { }, }); - const verifiedClientAssertion = verifyClientAssertion(jws, undefined); + const verifiedClientAssertion = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(verifiedClientAssertion.data?.payload.digest).toBeUndefined(); }); @@ -401,7 +470,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ customClaims: { digest: { alg: "alg", invalidProp: true } }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0].code).toEqual(digestClaimNotFound("").code); @@ -413,7 +486,11 @@ describe("validation test", async () => { digest: { alg: "SHA256", value: "string of wrong length" }, }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidHashLength("SHA256")); @@ -425,7 +502,11 @@ describe("validation test", async () => { digest: { alg: "wrong alg", value: value64chars }, }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidHashAlgorithm()); @@ -437,7 +518,11 @@ describe("validation test", async () => { digest: { alg: "wrong alg", value: "string of wrong length" }, }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(2); expect(errors).toEqual([ @@ -451,7 +536,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ customHeader: { alg: undefined }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(algorithmNotFound()); @@ -462,7 +551,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ customHeader: { alg: "RS512" }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(algorithmNotAllowed(notAllowedAlg)); @@ -472,7 +565,11 @@ describe("validation test", async () => { const { jws } = await getMockClientAssertion({ customHeader: { kid: "not a valid kid" }, }); - const { errors } = verifyClientAssertion(jws, undefined); + const { errors } = verifyClientAssertion( + jws, + undefined, + expectedAudiences + ); expect(errors).toBeDefined(); expect(errors).toHaveLength(1); expect(errors![0]).toEqual(invalidKidFormat()); @@ -777,7 +874,8 @@ describe("validation test", async () => { standardClaimsOverride: { purposeId: generateId() }, }) ).jws, - undefined + undefined, + expectedAudiences ); if (!mockClientAssertion) { fail(); @@ -801,7 +899,8 @@ describe("validation test", async () => { standardClaimsOverride: { purposeId: generateId() }, }) ).jws, - undefined + undefined, + expectedAudiences ); if (!mockClientAssertion) { fail(); @@ -822,7 +921,8 @@ describe("validation test", async () => { }; const { data: mockClientAssertion } = verifyClientAssertion( (await getMockClientAssertion()).jws, - undefined + undefined, + expectedAudiences ); if (!mockClientAssertion) { fail(); @@ -848,7 +948,8 @@ describe("validation test", async () => { customClaims: { purposeId: mockConsumerClient.GSIPK_purposeId }, }) ).jws, - undefined + undefined, + expectedAudiences ); if (!mockClientAssertion) { fail(); @@ -872,7 +973,8 @@ describe("validation test", async () => { standardClaimsOverride: { purposeId: undefined }, }) ).jws, - undefined + undefined, + expectedAudiences ); if (!mockClientAssertion) { fail(); @@ -898,7 +1000,8 @@ describe("validation test", async () => { standardClaimsOverride: { purposeId: undefined }, }) ).jws, - undefined + undefined, + expectedAudiences ); if (!mockClientAssertion) { fail(); @@ -915,4 +1018,153 @@ describe("validation test", async () => { ]); }); }); + + describe("validateAudience", () => { + describe("expectedAudiences is a one item array", () => { + it("should succeed if the expected audiences contain the received audience (string)", () => { + const receivedAudiences = "aud1"; + const expectedAudiences = ["aud1"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: receivedAudiences, + errors: undefined, + }); + }); + + it("should return error if the expected audiences don't contain the received audience (string)", () => { + const receivedAudiences = "aud2"; + const expectedAudiences = ["aud1"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [invalidAudience(receivedAudiences)], + }); + }); + + it("should return error if the received audience is undefined", () => { + const receivedAudiences = undefined; + const expectedAudiences = ["aud1"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [audienceNotFound()], + }); + }); + + it("should return error if the expected audiences don't contain the received audience (comma separated string)", () => { + const receivedAudiences = "aud1, aud2"; + const expectedAudiences = ["aud1"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [invalidAudience(receivedAudiences)], + }); + }); + + it("should return error if the intersection between the expected audiences and the received audiences is empty (array)", () => { + const receivedAudiences = ["aud2"]; + const expectedAudiences = ["aud1"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [invalidAudience(receivedAudiences)], + }); + }); + + it("should succeed if the intersection between the expected audiences and the received audiences is not empty (array)", () => { + const receivedAudiences = ["aud1", "aud2"]; + const expectedAudiences = ["aud1"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: receivedAudiences, + errors: undefined, + }); + }); + }); + + describe("expectedAudiences is a two items array", () => { + it("should succeed if the expected audiences contain the received audience (string)", () => { + const receivedAudiences = "aud1"; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: receivedAudiences, + errors: undefined, + }); + }); + + it("should return error if the expected audiences don't contain the received audience (string)", () => { + const receivedAudiences = "aud3"; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [invalidAudience(receivedAudiences)], + }); + }); + + it("should return error if the expected audiences don't contain the received audience (comma separated string)", () => { + const receivedAudiences = "aud1, aud2"; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [invalidAudience(receivedAudiences)], + }); + }); + + it("should succeed if the expected audiences contain the received audiences (array)", () => { + const receivedAudiences = ["aud1"]; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: receivedAudiences, + errors: undefined, + }); + }); + + it("should return error if the intersection between the expected audiences and the received audiences (array) is empty", () => { + const receivedAudiences = ["aud3"]; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: undefined, + errors: [invalidAudience(receivedAudiences)], + }); + }); + + it("should succeed if the expected audiences match the received audiences (array)", () => { + const receivedAudiences = ["aud1", "aud2"]; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: receivedAudiences, + errors: undefined, + }); + }); + + it("should succeed if the intersection between the expected audiences and the received audiences (array) is not empty", () => { + const receivedAudiences = ["aud1", "aud3"]; + const expectedAudiences = ["aud1", "aud2"]; + expect( + validateAudience(receivedAudiences, expectedAudiences) + ).toMatchObject({ + data: receivedAudiences, + errors: undefined, + }); + }); + }); + }); }); diff --git a/packages/commons/src/interop-token/interopTokenService.ts b/packages/commons/src/interop-token/interopTokenService.ts index f4fec60d4f..fdf5ac2f1b 100644 --- a/packages/commons/src/interop-token/interopTokenService.ts +++ b/packages/commons/src/interop-token/interopTokenService.ts @@ -155,7 +155,7 @@ export class InteropTokenGenerator { ); } - const currentTimestamp = Date.now(); + const currentTimestamp = dateToSeconds(new Date()); const header: InteropJwtHeader = { alg: "RS256", @@ -167,13 +167,13 @@ export class InteropTokenGenerator { const payload: InteropJwtApiPayload = { jti: generateId(), iss: this.config.generatedInteropTokenIssuer, - aud: [this.config.generatedInteropTokenM2MAudience], + aud: this.toJwtAudience(this.config.generatedInteropTokenM2MAudience), + client_id: sub, sub, iat: currentTimestamp, nbf: currentTimestamp, exp: - currentTimestamp + - this.config.generatedInteropTokenM2MDurationSeconds * 1000, + currentTimestamp + this.config.generatedInteropTokenM2MDurationSeconds, [ORGANIZATION_ID_CLAIM]: consumerId, [ROLE_CLAIM]: GENERATED_INTEROP_TOKEN_M2M_ROLE, }; @@ -214,7 +214,7 @@ export class InteropTokenGenerator { ); } - const currentTimestamp = Date.now(); + const currentTimestamp = dateToSeconds(new Date()); const header: InteropJwtHeader = { alg: "RS256", @@ -226,7 +226,8 @@ export class InteropTokenGenerator { const payload: InteropJwtConsumerPayload = { jti: generateId(), iss: this.config.generatedInteropTokenIssuer, - aud: audience, + aud: this.toJwtAudience(audience), + client_id: sub, sub, iat: currentTimestamp, nbf: currentTimestamp, @@ -254,7 +255,11 @@ export class InteropTokenGenerator { keyId, }: { header: InteropJwtHeader; - payload: InteropJwtPayload | SessionJwtPayload | InteropJwtConsumerPayload; + payload: + | InteropJwtPayload + | SessionJwtPayload + | InteropJwtConsumerPayload + | InteropJwtApiPayload; keyId: string; }): Promise { const serializedToken = `${b64UrlEncode( @@ -278,4 +283,7 @@ export class InteropTokenGenerator { return `${serializedToken}.${jwtSignature}`; } + + private toJwtAudience = (input: string | string[]): string | string[] => + Array.isArray(input) && input.length === 1 ? input[0] : input; } diff --git a/packages/commons/src/interop-token/models.ts b/packages/commons/src/interop-token/models.ts index 9054a5d483..c58ea47655 100644 --- a/packages/commons/src/interop-token/models.ts +++ b/packages/commons/src/interop-token/models.ts @@ -1,4 +1,9 @@ -import { ClientAssertionDigest } from "pagopa-interop-models"; +import { + ClientAssertionDigest, + ClientId, + PurposeId, + TenantId, +} from "pagopa-interop-models"; import { z } from "zod"; export const ORGANIZATION = "organization"; @@ -26,21 +31,23 @@ export interface InteropJwtHeader { export type InteropJwtCommonPayload = { jti: string; iss: string; - aud: string[]; + aud: string[] | string; iat: number; nbf: number; exp: number; }; export type InteropJwtConsumerPayload = InteropJwtCommonPayload & { - sub: string; - [PURPOSE_ID_CLAIM]: string; + client_id: ClientId; + sub: ClientId; + [PURPOSE_ID_CLAIM]: PurposeId; digest?: ClientAssertionDigest; }; export type InteropJwtApiPayload = InteropJwtCommonPayload & { - sub: string; - [ORGANIZATION_ID_CLAIM]: string; + client_id: ClientId; + sub: ClientId; + [ORGANIZATION_ID_CLAIM]: TenantId; [ROLE_CLAIM]: string; }; diff --git a/packages/commons/src/utils/date.ts b/packages/commons/src/utils/date.ts index 8e4dcace25..1a284499d6 100644 --- a/packages/commons/src/utils/date.ts +++ b/packages/commons/src/utils/date.ts @@ -32,3 +32,11 @@ export function timeAtRomeZone(date: Date): string { export function dateToSeconds(date: Date): number { return Math.floor(date.getTime() / 1000); } + +export const secondsToMilliseconds = (timestamp: number): number => { + if (timestamp.toString().length === 10) { + return timestamp * 1000; + } + + return timestamp; +}; From c7b36e37f65cd29676e2a1b60f7d371e9c378f54 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Mon, 9 Dec 2024 16:10:29 +0100 Subject: [PATCH 072/126] Removing delegation info from state processors (#1260) --- .../src/services/agreementService.ts | 4 +-- .../src/services/agreementStateProcessor.ts | 28 +++++-------------- .../services/agreementSuspensionProcessor.ts | 3 +- .../test/upgradeAgreement.test.ts | 2 +- 4 files changed, 10 insertions(+), 27 deletions(-) diff --git a/packages/agreement-process/src/services/agreementService.ts b/packages/agreement-process/src/services/agreementService.ts index 489566295f..959d325bee 100644 --- a/packages/agreement-process/src/services/agreementService.ts +++ b/packages/agreement-process/src/services/agreementService.ts @@ -410,13 +410,11 @@ export function agreementServiceBuilder( agreement.data.eserviceId, readModelService ); - const delegateProducerId = activeProducerDelegation?.delegateId; const nextStateByAttributes = nextStateByAttributesFSM( agreement.data, descriptor, - consumer, - delegateProducerId + consumer ); const suspendedByPlatform = suspendedByPlatformFlag( diff --git a/packages/agreement-process/src/services/agreementStateProcessor.ts b/packages/agreement-process/src/services/agreementStateProcessor.ts index 1ed963afbd..1be19adbf5 100644 --- a/packages/agreement-process/src/services/agreementStateProcessor.ts +++ b/packages/agreement-process/src/services/agreementStateProcessor.ts @@ -42,13 +42,9 @@ const { const nextStateFromDraft = ( agreement: Agreement, descriptor: Descriptor, - tenant: Tenant | CompactTenant, - delegateProducerId: TenantId | undefined + tenant: Tenant | CompactTenant ): AgreementState => { - if ( - agreement.consumerId === agreement.producerId || - agreement.consumerId === delegateProducerId - ) { + if (agreement.consumerId === agreement.producerId) { return active; } if (!certifiedAttributesSatisfied(descriptor.attributes, tenant.attributes)) { @@ -98,13 +94,9 @@ const nextStateFromPending = ( const nextStateFromActiveOrSuspended = ( agreement: Agreement, descriptor: Descriptor, - tenant: Tenant | CompactTenant, - delegateProducerId: TenantId | undefined + tenant: Tenant | CompactTenant ): AgreementState => { - if ( - agreement.consumerId === agreement.producerId || - agreement.consumerId === delegateProducerId - ) { + if (agreement.consumerId === agreement.producerId) { return active; } if ( @@ -134,23 +126,17 @@ const nextStateFromMissingCertifiedAttributes = ( export const nextStateByAttributesFSM = ( agreement: Agreement, descriptor: Descriptor, - tenant: Tenant | CompactTenant, - delegateProducerId?: TenantId | undefined + tenant: Tenant | CompactTenant ): AgreementState => match(agreement.state) .with(agreementState.draft, () => - nextStateFromDraft(agreement, descriptor, tenant, delegateProducerId) + nextStateFromDraft(agreement, descriptor, tenant) ) .with(agreementState.pending, () => nextStateFromPending(agreement, descriptor, tenant) ) .with(agreementState.active, agreementState.suspended, () => - nextStateFromActiveOrSuspended( - agreement, - descriptor, - tenant, - delegateProducerId - ) + nextStateFromActiveOrSuspended(agreement, descriptor, tenant) ) .with(agreementState.archived, () => archived) .with(agreementState.missingCertifiedAttributes, () => diff --git a/packages/agreement-process/src/services/agreementSuspensionProcessor.ts b/packages/agreement-process/src/services/agreementSuspensionProcessor.ts index 504ed9bc50..ef8e51d916 100644 --- a/packages/agreement-process/src/services/agreementSuspensionProcessor.ts +++ b/packages/agreement-process/src/services/agreementSuspensionProcessor.ts @@ -49,8 +49,7 @@ export function createSuspensionUpdatedAgreement({ const nextStateByAttributes = nextStateByAttributesFSM( agreement, descriptor, - consumer, - producerDelegation?.delegateId + consumer ); const suspendedByConsumer = suspendedByConsumerFlag( diff --git a/packages/agreement-process/test/upgradeAgreement.test.ts b/packages/agreement-process/test/upgradeAgreement.test.ts index d28b90217d..08bcc5569e 100644 --- a/packages/agreement-process/test/upgradeAgreement.test.ts +++ b/packages/agreement-process/test/upgradeAgreement.test.ts @@ -554,7 +554,7 @@ describe("upgrade Agreement", () => { } }); - it("should succeed with valid Verified, Certified, and Declared attributes when consumer and producer are different and requester is a delegate", async () => { + it("should succeed with valid Verified, Certified, and Declared attributes when consumer and producer are different, requester is consumer, an active producer delegation exists and is taken into account for the PDF contract generation", async () => { const producer = getMockTenant(); const validVerifiedTenantAttribute = { From 928865d2ecc13fb528eec3df8de55995d6a66319 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 11 Dec 2024 10:16:13 +0100 Subject: [PATCH 073/126] PIN-5818 - Fix purpose risk analysis visibility for producer delegate (#1276) --- .../src/services/purposeService.ts | 18 +- .../test/getPurposeById.test.ts | 177 ++++++++++++++++++ 2 files changed, 192 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index 6b79c90547..e27b410a64 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -42,6 +42,7 @@ import { RiskAnalysis, CorrelationId, delegationKind, + Delegation, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -241,11 +242,18 @@ export function purposeServiceBuilder( readModelService ); + const activeProducerDelegation = + await readModelService.getActiveDelegation( + purpose.data.eserviceId, + delegationKind.delegatedProducer + ); + return authorizeRiskAnalysisForm({ purpose: purpose.data, producerId: eservice.producerId, organizationId, tenantKind: await retrieveTenantKind(organizationId, readModelService), + activeProducerDelegation, }); }, async getRiskAnalysisDocument({ @@ -780,7 +788,6 @@ export function purposeServiceBuilder( return newPurposeVersion; }, - async activatePurposeVersion({ purposeId, versionId, @@ -1005,7 +1012,6 @@ export function purposeServiceBuilder( await repository.createEvent(event); return updatedPurposeVersion; }, - async createPurpose( purposeSeed: purposeApi.PurposeSeed, organizationId: TenantId, @@ -1321,13 +1327,19 @@ const authorizeRiskAnalysisForm = ({ producerId, organizationId, tenantKind, + activeProducerDelegation, }: { purpose: Purpose; producerId: TenantId; organizationId: TenantId; tenantKind: TenantKind; + activeProducerDelegation: Delegation | undefined; }): { purpose: Purpose; isRiskAnalysisValid: boolean } => { - if (organizationId === purpose.consumerId || organizationId === producerId) { + if ( + organizationId === purpose.consumerId || + organizationId === producerId || + organizationId === activeProducerDelegation?.delegateId + ) { if (purposeIsDraft(purpose)) { const isRiskAnalysisValid = isRiskAnalysisFormValid( purpose.riskAnalysisForm, diff --git a/packages/purpose-process/test/getPurposeById.test.ts b/packages/purpose-process/test/getPurposeById.test.ts index 2630bb12f1..52a547670d 100644 --- a/packages/purpose-process/test/getPurposeById.test.ts +++ b/packages/purpose-process/test/getPurposeById.test.ts @@ -3,6 +3,8 @@ import { getMockTenant, getMockPurpose, writeInReadmodel, + getMockValidRiskAnalysisForm, + getMockDelegation, } from "pagopa-interop-commons-test"; import { tenantKind, @@ -13,6 +15,10 @@ import { EServiceId, TenantId, toReadModelTenant, + EService, + Delegation, + delegationKind, + delegationState, } from "pagopa-interop-models"; import { describe, expect, it } from "vitest"; import { genericLogger } from "pagopa-interop-commons"; @@ -28,6 +34,7 @@ import { eservices, purposeService, tenants, + delegations, } from "./utils.js"; describe("getPurposeById", () => { @@ -62,6 +69,176 @@ describe("getPurposeById", () => { isRiskAnalysisValid: false, }); }); + it("should get the purpose with the risk analysis form if the requester is the active e-service producer", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockEService: EService = { + ...getMockEService(), + producerId: mockTenant.id, + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + }; + + await addOnePurpose(mockPurpose1); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(toReadModelTenant(mockTenant), tenants); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + mockTenant.id, + genericLogger + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: true, + }); + }); + it("should get the purpose with the risk analysis form if the requester is the purpose agreement consumer", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockEService: EService = { + ...getMockEService(), + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + consumerId: mockTenant.id, + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + }; + + await addOnePurpose(mockPurpose1); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(toReadModelTenant(mockTenant), tenants); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + mockTenant.id, + genericLogger + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: true, + }); + }); + it("should get the purpose with the risk analysis form if the requester is an e-service delegated producer", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockEService: EService = { + ...getMockEService(), + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + }; + + const mockProducerDelegation: Delegation = { + ...getMockDelegation({ kind: delegationKind.delegatedProducer }), + delegatorId: mockEService.producerId, + eserviceId: mockEService.id, + delegateId: mockTenant.id, + state: delegationState.active, + }; + + await addOnePurpose(mockPurpose1); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(toReadModelTenant(mockTenant), tenants); + await writeInReadmodel(mockProducerDelegation, delegations); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + mockTenant.id, + genericLogger + ); + expect(result).toMatchObject({ + purpose: mockPurpose1, + isRiskAnalysisValid: true, + }); + }); + it("should get the purpose without the risk analysis form if the requester is not the producer nor the consumer", async () => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockEService: EService = { + ...getMockEService(), + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + }; + + await addOnePurpose(mockPurpose1); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(toReadModelTenant(mockTenant), tenants); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + mockTenant.id, + genericLogger + ); + expect(result).toMatchObject({ + purpose: { ...mockPurpose1, riskAnalysisForm: undefined }, + isRiskAnalysisValid: false, + }); + }); + it.each( + Object.values(delegationState).filter((s) => s !== delegationState.active) + )( + "should get the purpose without the risk analysis form if the requester has a producer delegation with state %s with the e-service purpose", + async (delegationState) => { + const mockTenant = { + ...getMockTenant(), + kind: tenantKind.PA, + }; + + const mockEService: EService = { + ...getMockEService(), + }; + const mockPurpose1: Purpose = { + ...getMockPurpose(), + eserviceId: mockEService.id, + riskAnalysisForm: getMockValidRiskAnalysisForm(tenantKind.PA), + }; + + const mockProducerDelegation: Delegation = { + ...getMockDelegation({ kind: delegationKind.delegatedProducer }), + delegatorId: mockEService.producerId, + eserviceId: mockEService.id, + delegateId: mockTenant.id, + state: delegationState, + }; + + await addOnePurpose(mockPurpose1); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(toReadModelTenant(mockTenant), tenants); + await writeInReadmodel(mockProducerDelegation, delegations); + + const result = await purposeService.getPurposeById( + mockPurpose1.id, + mockTenant.id, + genericLogger + ); + expect(result).toMatchObject({ + purpose: { ...mockPurpose1, riskAnalysisForm: undefined }, + isRiskAnalysisValid: false, + }); + } + ); it("should throw purposeNotFound if the purpose doesn't exist", async () => { const notExistingId: PurposeId = generateId(); const mockTenant = getMockTenant(); From 0982310cdbc19f88b8f7530b0ec55f1a490a8d5e Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 11 Dec 2024 10:30:32 +0100 Subject: [PATCH 074/126] Fix multiple agreement returned from query when multiple delegations on the same e-service (#1278) --- packages/agreement-process/src/services/readModelService.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/agreement-process/src/services/readModelService.ts b/packages/agreement-process/src/services/readModelService.ts index 7dd49e377f..59ca7f34c9 100644 --- a/packages/agreement-process/src/services/readModelService.ts +++ b/packages/agreement-process/src/services/readModelService.ts @@ -306,12 +306,6 @@ function getDelegateAgreementsFilters(producerIds: TenantId[] | undefined) { as: "delegations", }, }, - { - $unwind: { - path: "$delegations", - preserveNullAndEmptyArrays: true, - }, - }, { $match: { $or: [ From 8b144a675e70c0ae30b042ce78a12f7b4563b98f Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 11 Dec 2024 15:36:26 +0100 Subject: [PATCH 075/126] Fix merge problems --- .../agreement-outbound-writer/package.json | 2 +- packages/authorization-updater/src/index.ts | 1 - packages/catalog-outbound-writer/package.json | 2 +- .../src/services/catalogService.ts | 9 +- .../src/interfaceExporterV2.ts | 1 + packages/purpose-outbound-writer/package.json | 2 +- packages/tenant-outbound-writer/package.json | 2 +- pnpm-lock.yaml | 10748 +++++++++------- 8 files changed, 6223 insertions(+), 4544 deletions(-) diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index cfd8df6915..ec1964167f 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11a", + "@pagopa/interop-outbound-models": "1.0.11d", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/authorization-updater/src/index.ts b/packages/authorization-updater/src/index.ts index 9b7878ea4e..5c82d0c844 100644 --- a/packages/authorization-updater/src/index.ts +++ b/packages/authorization-updater/src/index.ts @@ -135,7 +135,6 @@ export async function sendCatalogAuthUpdate( "EServiceRiskAnalysisAdded", "EServiceRiskAnalysisUpdated", "EServiceRiskAnalysisDeleted", - "EServiceDescriptorQuotasUpdated", "EServiceDescriptorAttributesUpdated", "EServiceDescriptionUpdated", "EServiceDescriptorDelegateSubmitted", diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 25ff33e1b5..262c62a0c6 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11a", + "@pagopa/interop-outbound-models": "1.0.11d", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 23e4c3470e..69706334b8 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -1901,7 +1901,12 @@ export function catalogServiceBuilder( const eservice = await retrieveEService(eserviceId, readModelService); - assertRequesterAllowed(eservice.data.producerId, authData); + await assertRequesterIsDelegateProducerOrProducer( + eservice.data.producerId, + eserviceId, + authData, + readModelService + ); const descriptor = retrieveDescriptor(descriptorId, eservice); @@ -1909,7 +1914,7 @@ export function catalogServiceBuilder( descriptor.state !== descriptorState.published && descriptor.state !== descriptorState.suspended ) { - throw notValidDescriptor(descriptorId, descriptor.state); + throw notValidDescriptorState(descriptorId, descriptor.state); } /** diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts index 396755c684..640dd3e179 100644 --- a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts +++ b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts @@ -58,6 +58,7 @@ export async function exportInterfaceV2( { type: "EServiceDescriptionUpdated" }, { type: "EServiceDescriptorDelegateSubmitted" }, { type: "EServiceDescriptorDelegatorRejected" }, + { type: "EServiceDescriptorAttributesUpdated" }, () => undefined ) .exhaustive(); diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index 4b61fe333d..ef27a5a998 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11a", + "@pagopa/interop-outbound-models": "1.0.11d", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 02f287a2c1..15ea0468e5 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11a", + "@pagopa/interop-outbound-models": "1.0.11d", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a01bb606f2..46cb0f632b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,7 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: - autoInstallPeers: true + autoInstallPeers: false excludeLinksFromLockfile: false importers: @@ -11,9 +11,12 @@ importers: '@tsconfig/node-lts': specifier: 20.1.3 version: 20.1.3 + '@tsconfig/strictest': + specifier: 2.0.5 + version: 2.0.5 turbo: - specifier: 2.0.4 - version: 2.0.4 + specifier: 2.2.3 + version: 2.2.3 packages/agreement-email-sender: dependencies: @@ -44,13 +47,10 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 axios: specifier: 1.7.4 version: 1.7.4 @@ -66,15 +66,12 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -101,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.13 - version: 1.0.13 + specifier: 1.0.11d + version: 1.0.11-d '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -134,15 +131,15 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -152,15 +149,12 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -203,13 +197,10 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -219,15 +210,12 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -239,7 +227,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -251,7 +239,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-agreement-lifecycle: specifier: workspace:* version: link:../agreement-lifecycle @@ -267,9 +255,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -277,21 +262,24 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 cpx2: specifier: 7.0.1 version: 7.0.1 + date-fns: + specifier: 3.6.0 + version: 3.6.0 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -310,9 +298,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -353,15 +341,15 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -371,15 +359,12 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -410,7 +395,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -468,7 +453,7 @@ importers: version: 10.1.0(openapi-types@12.1.3) '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -478,6 +463,9 @@ importers: handlebars: specifier: 4.7.7 version: 4.7.7 + openapi-types: + specifier: 12.1.3 + version: 12.1.3 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -487,9 +475,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -501,7 +489,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -529,7 +517,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -542,9 +530,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -559,7 +547,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -574,7 +562,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -593,9 +581,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -608,7 +593,7 @@ importers: version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -618,9 +603,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -630,9 +612,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -672,7 +654,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -685,9 +667,70 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/authorization-platformstate-writer: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.693.0 + version: 3.693.0 + '@aws-sdk/util-dynamodb': + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -702,7 +745,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -717,7 +760,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -742,7 +785,89 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/authorization-server: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.693.0 + version: 3.693.0 + '@aws-sdk/client-kms': + specifier: 3.693.0 + version: 3.693.0 + '@aws-sdk/util-dynamodb': + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) + axios: + specifier: 1.7.4 + version: 1.7.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + express: + specifier: 4.20.0 + version: 4.20.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 + pagopa-interop-api-clients: + specifier: workspace:* + version: link:../api-clients + pagopa-interop-client-assertion-validation: + specifier: workspace:* + version: link:../client-assertion-validation + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -752,18 +877,18 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 + jose: + specifier: 5.9.4 + version: 5.9.4 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.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -806,22 +931,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -831,9 +950,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -854,7 +973,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) adm-zip: specifier: 0.5.15 version: 0.5.15 @@ -897,9 +1016,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 yaml: specifier: 2.4.5 version: 2.4.5 @@ -909,7 +1025,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 @@ -925,9 +1041,6 @@ importers: '@types/qs': specifier: 6.9.15 version: 6.9.15 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -937,9 +1050,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -950,8 +1063,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.13 - version: 1.0.13 + specifier: 1.0.11d + version: 1.0.11-d '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -983,15 +1096,15 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1001,15 +1114,12 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1052,13 +1162,10 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1068,15 +1175,15 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1088,7 +1195,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -1100,7 +1207,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -1116,9 +1223,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -1131,7 +1235,7 @@ importers: version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1141,9 +1245,6 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1159,9 +1260,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1201,13 +1302,10 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -1220,15 +1318,12 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1308,7 +1403,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1354,22 +1449,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1379,9 +1468,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1408,7 +1497,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -1421,6 +1510,9 @@ importers: date-fns-tz: specifier: 3.1.3 version: 3.1.3(date-fns@3.6.0) + express: + specifier: 4.20.0 + version: 4.20.0 handlebars: specifier: 4.7.8 version: 4.7.8 @@ -1435,7 +1527,7 @@ importers: version: 2.2.4 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) nodemailer: specifier: 6.9.14 version: 6.9.14 @@ -1457,9 +1549,6 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 winston: specifier: 3.13.0 version: 3.13.0 @@ -1482,9 +1571,6 @@ importers: '@types/nodemailer': specifier: 6.4.15 version: 6.4.15 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 cpx2: specifier: 7.0.1 version: 7.0.1 @@ -1512,9 +1598,12 @@ importers: '@aws-sdk/util-dynamodb': specifier: 3.693.0 version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1524,9 +1613,6 @@ importers: '@types/jsonwebtoken': specifier: 9.0.6 version: 9.0.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -1539,6 +1625,9 @@ importers: dotenv-flow: specifier: 4.1.0 version: 4.1.0 + jose: + specifier: 5.9.4 + version: 5.9.4 jsonwebtoken: specifier: 9.0.2 version: 9.0.2 @@ -1560,9 +1649,6 @@ importers: typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -1593,28 +1679,22 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1629,7 +1709,7 @@ importers: version: 4.1.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1642,7 +1722,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -1662,14 +1742,17 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) - packages/dtd-catalog-exporter: + packages/datalake-interface-exporter: dependencies: dotenv-flow: specifier: 4.1.0 version: 4.1.0 - mongodb: - specifier: 6.7.0 - version: 6.7.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 @@ -1686,15 +1769,18 @@ importers: specifier: 3.23.8 version: 3.23.8 devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - eslint: - specifier: 8.57.0 - version: 8.57.0 prettier: specifier: 2.8.8 version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 tsx: specifier: 4.19.1 version: 4.19.1 @@ -1705,27 +1791,210 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) - packages/eservice-descriptors-archiver: + packages/delegation-process: dependencies: '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) + '@zodios/express': + specifier: 10.6.1 + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8) dotenv-flow: specifier: 4.1.0 version: 4.1.0 - kafka-iam-auth: - specifier: workspace:* - version: link:../kafka-iam-auth - kafkajs: - specifier: 2.2.4 - version: 2.2.4 + express: + specifier: 4.19.2 + version: 4.19.2 + mongodb: + specifier: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) + openapi-zod-client: + specifier: 1.18.1 + version: 1.18.1 pagopa-interop-api-clients: specifier: workspace:* version: link:../api-clients pagopa-interop-commons: specifier: workspace:* version: link:../commons - pagopa-interop-commons-test: + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@anatine/zod-mock': + specifier: 3.13.4 + version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + '@types/express': + specifier: 4.17.21 + version: 4.17.21 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + cpx2: + specifier: 7.0.1 + version: 7.0.1 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + pg-promise: + specifier: 11.8.0 + version: 11.8.0 + prettier: + specifier: 2.8.8 + version: 2.8.8 + puppeteer: + specifier: 22.11.2 + version: 22.11.2(typescript@5.4.5) + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/delegation-readmodel-writer: + dependencies: + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/dtd-catalog-exporter: + dependencies: + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + mongodb: + specifier: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + eslint: + specifier: 8.57.0 + version: 8.57.0 + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + + packages/eservice-descriptors-archiver: + dependencies: + '@zodios/core': + specifier: 10.9.6 + version: 10.9.6(axios@1.7.4)(zod@3.23.8) + axios: + specifier: 1.7.4 + version: 1.7.4 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + kafka-iam-auth: + specifier: workspace:* + version: link:../kafka-iam-auth + kafkajs: + specifier: 2.2.4 + version: 2.2.4 + pagopa-interop-api-clients: + specifier: workspace:* + version: link:../api-clients + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test pagopa-interop-models: @@ -1734,22 +2003,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -1759,9 +2022,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1788,7 +2051,7 @@ importers: version: 4.5.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) pagopa-interop-commons: specifier: workspace:* version: link:../commons @@ -1820,9 +2083,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1859,16 +2122,13 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/adm-zip': specifier: 0.5.5 version: 0.5.5 @@ -1878,9 +2138,6 @@ importers: '@types/ssh2-sftp-client': specifier: 9.0.4 version: 9.0.4 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1890,9 +2147,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -1910,7 +2167,7 @@ importers: version: 3.693.0 aws-msk-iam-sasl-signer-js: specifier: 1.0.0 - version: 1.0.0(@aws-sdk/client-sso-oidc@3.693.0) + version: 1.0.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -1951,22 +2208,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -1976,9 +2227,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2000,15 +2251,9 @@ importers: '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 zod: specifier: 3.23.8 version: 3.23.8 @@ -2061,22 +2306,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 kafkajs: specifier: 2.2.4 version: 2.2.4 @@ -2092,9 +2331,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2128,7 +2367,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/html2json': specifier: 1.0.1 version: 1.0.1 @@ -2174,7 +2413,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2223,31 +2462,25 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@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.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2278,22 +2511,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2303,9 +2530,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2336,22 +2563,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2361,9 +2582,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2374,8 +2595,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.13 - version: 1.0.13 + specifier: 1.0.11d + version: 1.0.11-d '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2407,15 +2628,15 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2425,15 +2646,12 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2476,13 +2694,10 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 date-fns: specifier: 3.6.0 version: 3.6.0 @@ -2492,15 +2707,12 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) @@ -2512,7 +2724,7 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -2527,7 +2739,7 @@ importers: version: 4.7.8 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -2552,7 +2764,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -2574,9 +2786,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2607,22 +2819,16 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2632,9 +2838,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2668,28 +2874,22 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2700,8 +2900,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.13 - version: 1.0.13 + specifier: 1.0.11d + version: 1.0.11-d '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2733,15 +2933,15 @@ importers: '@anatine/zod-mock': specifier: 3.13.4 version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 pagopa-interop-commons-test: specifier: workspace:* version: link:../commons-test @@ -2751,30 +2951,24 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 - uuid: - specifier: 10.0.0 - version: 10.0.0 vitest: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) packages/tenant-process: dependencies: - '@types/uuid': - specifier: 9.0.8 - version: 9.0.8 '@zodios/core': specifier: 10.9.6 version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) axios: specifier: 1.7.4 version: 1.7.4 @@ -2786,7 +2980,7 @@ importers: version: 4.20.0 mongodb: specifier: 6.7.0 - version: 6.7.0 + version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) openapi-zod-client: specifier: 1.18.1 version: 1.18.1 @@ -2802,16 +2996,13 @@ importers: ts-pattern: specifier: 5.2.0 version: 5.2.0 - uuid: - specifier: 10.0.0 - version: 10.0.0 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) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/express': specifier: 4.17.21 version: 4.17.21 @@ -2830,9 +3021,9 @@ importers: testcontainers: specifier: 10.9.0 version: 10.9.0 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2872,7 +3063,7 @@ importers: devDependencies: '@pagopa/eslint-config': specifier: 3.0.0 - version: 3.0.0(typescript@5.4.5) + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) '@types/node': specifier: 20.14.6 version: 20.14.6 @@ -2882,9 +3073,9 @@ importers: prettier: specifier: 2.8.8 version: 2.8.8 - ts-node: - specifier: 10.9.2 - version: 10.9.2(@types/node@20.14.6)(typescript@5.4.5) + tsx: + specifier: 4.19.1 + version: 4.19.1 typescript: specifier: 5.4.5 version: 5.4.5 @@ -2892,44 +3083,4382 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) -packages: - - /@ampproject/remapping@2.3.0: - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - - /@anatine/zod-mock@3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8): - resolution: {integrity: sha512-yO/KeuyYsEDCTcQ+7CiRuY3dnafMHIZUMok6Ci7aERRCTQ+/XmsiPk/RnMx5wlLmWBTmX9kw+PavbMsjM+sAJA==} - peerDependencies: - '@faker-js/faker': ^7.0.0 || ^8.0.0 - zod: ^3.21.4 - dependencies: - '@faker-js/faker': 8.4.1 - randexp: 0.5.3 - zod: 3.23.8 - dev: true - - /@apidevtools/json-schema-ref-parser@9.0.6: - resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} + packages/token-details-persister: dependencies: - '@jsdevtools/ono': 7.1.3 - call-me-maybe: 1.0.2 - js-yaml: 3.14.1 - - /@apidevtools/openapi-schemas@2.1.0: - resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} - engines: {node: '>=10'} - - /@apidevtools/swagger-methods@3.0.2: - resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - - /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3): - resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} - peerDependencies: + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + date-fns: + specifier: 3.6.0 + version: 3.6.0 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@anatine/zod-mock@3.13.4': + resolution: {integrity: sha512-yO/KeuyYsEDCTcQ+7CiRuY3dnafMHIZUMok6Ci7aERRCTQ+/XmsiPk/RnMx5wlLmWBTmX9kw+PavbMsjM+sAJA==} + peerDependencies: + '@faker-js/faker': ^7.0.0 || ^8.0.0 + zod: ^3.21.4 + + '@apidevtools/json-schema-ref-parser@9.0.6': + resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@apidevtools/swagger-parser@10.1.0': + resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==} + peerDependencies: openapi-types: '>=7' + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/crc32c@5.2.0': + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + + '@aws-crypto/sha1-browser@5.2.0': + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@4.0.0': + resolution: {integrity: sha512-MHGJyjE7TX9aaqXj7zk2ppnFUOhaDs5sP+HtNS0evOxn72c+5njUmyJmpGd7TfyoDznZlHMmdo/xGUdu2NIjNQ==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@4.0.0': + resolution: {integrity: sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-cognito-identity@3.609.0': + resolution: {integrity: sha512-3kDTpia1iN/accayoH3MbZRbDvX2tzrKrBTU7wNNoazVrh+gOMS8KCOWrOB72F0V299l4FsfQhnl9BDMVrc1iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-dynamodb@3.693.0': + resolution: {integrity: sha512-EmgFoE/wAxiOq/sfO/VFGlmvfq0FexUO4IMURr3deIpU/AuCsuU87HJH/UodFdKu88ykNZxMfHHku6o6BV2dAA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-kms@3.693.0': + resolution: {integrity: sha512-9APrRDbScYqfCG89Trruf9+Bx39wlXQdwPEhluycpCF+bHYiTKdIXc4uix6hjbXzK0NKbgitus8bbMvsHX+Oxg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-s3@3.693.0': + resolution: {integrity: sha512-vgGI2e0Q6pzyhqfrSysi+sk/i+Nl+lMon67oqj/57RcCw9daL1/inpS+ADuwHpiPWkrg+U0bOXnmHjkLeTslJg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sesv2@3.693.0': + resolution: {integrity: sha512-5lQUWTxNsd933o1TvAsa564BeMv8IbhxHIgssCxf3ibnqUgI7S/R6Bk62KqlxjxcgWDXabQWQskejZ8cpupYqA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sqs@3.693.0': + resolution: {integrity: sha512-Nfmo6bgad26PP1B7MZP9kpxOw874cVEvJHXBXwkSLg6ZPAeU2cnvTEE++omHkzLeeB9MbpmvhQtdaUn4c7DYZQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso-oidc@3.609.0': + resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/client-sso-oidc@3.693.0': + resolution: {integrity: sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.693.0 + + '@aws-sdk/client-sso@3.609.0': + resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sso@3.693.0': + resolution: {integrity: sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.609.0': + resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/client-sts@3.693.0': + resolution: {integrity: sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.609.0': + resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/core@3.693.0': + resolution: {integrity: sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-cognito-identity@3.609.0': + resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.609.0': + resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-env@3.693.0': + resolution: {integrity: sha512-hMUZaRSF7+iBKZfBHNLihFs9zvpM1CB8MBOTnTp5NGCVkRYF3SB2LH+Kcippe0ats4qCyB1eEoyQX99rERp2iQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.609.0': + resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-http@3.693.0': + resolution: {integrity: sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-ini@3.609.0': + resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/credential-provider-ini@3.693.0': + resolution: {integrity: sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.693.0 + + '@aws-sdk/credential-provider-node@3.609.0': + resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-node@3.693.0': + resolution: {integrity: sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.609.0': + resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-process@3.693.0': + resolution: {integrity: sha512-cvxQkrTWHHjeHrPlj7EWXPnFSq8x7vMx+Zn1oTsMpCY445N9KuzjfJTkmNGwU2GT6rSZI9/0MM02aQvl5bBBTQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.609.0': + resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-sso@3.693.0': + resolution: {integrity: sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.609.0': + resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.609.0 + + '@aws-sdk/credential-provider-web-identity@3.693.0': + resolution: {integrity: sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sts': ^3.693.0 + + '@aws-sdk/credential-providers@3.609.0': + resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/endpoint-cache@3.693.0': + resolution: {integrity: sha512-/zK0ZZncBf5FbTfo8rJMcQIXXk4Ibhe5zEMiwFNivVPR2uNC0+oqfwXz7vjxwY0t6BPE3Bs4h9uFEz4xuGCY6w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-bucket-endpoint@3.693.0': + resolution: {integrity: sha512-cPIa+lxMYiFRHtxKfNIVSFGO6LSgZCk42pu3d7KGwD6hu6vXRD5B2/DD3rPcEH1zgl2j0Kx1oGAV7SRXKHSFag==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-endpoint-discovery@3.693.0': + resolution: {integrity: sha512-OG8WM8OzYuAt3Ueb8TZoBgA+vqNgPaXksHhiy8SFTQxNamSMMRvKrDSBbdUuV96mq0lcJq1mFgJ4oRXJ1HPh6A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-expect-continue@3.693.0': + resolution: {integrity: sha512-MuK/gsJWpHz6Tv0CqTCS+QNOxLa2RfPh1biVCu/uO3l7kA0TjQ/C+tfgKvLXeH103tuDrOVINK+bt2ENmI3SWg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-flexible-checksums@3.693.0': + resolution: {integrity: sha512-xkS6zjuE11ob93H9t65kHzphXcUMnN2SmIm2wycUPg+hi8Q6DJA6U2p//6oXkrr9oHy1QvwtllRd7SAd63sFKQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.609.0': + resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-host-header@3.693.0': + resolution: {integrity: sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-location-constraint@3.693.0': + resolution: {integrity: sha512-eDAExTZ9uNIP7vs2JCVCOuWJauGueisBSn+Ovt7UvvuEUp6KOIJqn8oFxWmyUQu2GvbG4OcaTLgbqD95YHTB0Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.609.0': + resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-logger@3.693.0': + resolution: {integrity: sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.609.0': + resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.693.0': + resolution: {integrity: sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-s3@3.693.0': + resolution: {integrity: sha512-5A++RBjJ3guyq5pbYs+Oq5hMlA8CK2OWaHx09cxVfhHWl/RoaY8DXrft4gnhoUEBrrubyMw7r9j7RIMLvS58kg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-sdk-sqs@3.693.0': + resolution: {integrity: sha512-txpz7gKX6b8NE+VdIy3UQzEXCNM0ubpuaEmkr1yReNh0x8hqslxtQyjhn6tqKvAABQ/VnKSJbbqMzsi710fR4Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-ssec@3.693.0': + resolution: {integrity: sha512-Ro5vzI7SRgEeuoMk3fKqFjGv6mG4c7VsSCDwnkiasmafQFBTPvUIpgmu2FXMHqW/OthvoiOzpSrlJ9Bwlx2f8A==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.609.0': + resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/middleware-user-agent@3.693.0': + resolution: {integrity: sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.609.0': + resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/region-config-resolver@3.693.0': + resolution: {integrity: sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/s3-request-presigner@3.693.0': + resolution: {integrity: sha512-I/TCM43kZn1xb+EWMAjkcisDVrq3mYsu0ZFP81J9K/PM6n3s9bK04jaY56c3pCl6btigIOHhreutYSRRBJsCDw==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.693.0': + resolution: {integrity: sha512-s7zbbsoVIriTR4ZGaateKuTqz6ddpazAyHvjk7I9kd+NvGNPiuAI18UdbuiiRI6K5HuYKf1ah6mKWFGPG15/kQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/token-providers@3.609.0': + resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.609.0 + + '@aws-sdk/token-providers@3.693.0': + resolution: {integrity: sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-sso-oidc': ^3.693.0 + + '@aws-sdk/types@3.609.0': + resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/types@3.692.0': + resolution: {integrity: sha512-RpNvzD7zMEhiKgmlxGzyXaEcg2khvM7wd5sSHVapOcrde1awQSOMGI4zKBQ+wy5TnDfrm170ROz/ERLYtrjPZA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-arn-parser@3.693.0': + resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-dynamodb@3.693.0': + resolution: {integrity: sha512-lwJnlQndVS7cGN1UmtG4s6IdVW3U/WheJ14Xc/mUeAH3vga7GTY3hGi+Jp1TbnaYQkad589tU+NQ5KUW7V8ZjA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@aws-sdk/client-dynamodb': ^3.693.0 + + '@aws-sdk/util-endpoints@3.609.0': + resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-endpoints@3.693.0': + resolution: {integrity: sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-format-url@3.609.0': + resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-format-url@3.693.0': + resolution: {integrity: sha512-0O4fSq45GOwC89Os0f92z9kK1AV22+W980O+v+GkMLUkRG7/nsIJkq1LKiIPV+sbC+KC/HmW4yThxFzHO7GDxA==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-locate-window@3.568.0': + resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} + engines: {node: '>=16.0.0'} + + '@aws-sdk/util-user-agent-browser@3.609.0': + resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} + + '@aws-sdk/util-user-agent-browser@3.693.0': + resolution: {integrity: sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==} + + '@aws-sdk/util-user-agent-node@3.609.0': + resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-user-agent-node@3.693.0': + resolution: {integrity: sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==} + engines: {node: '>=16.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + + '@aws-sdk/util-utf8-browser@3.259.0': + resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + + '@aws-sdk/xml-builder@3.693.0': + resolution: {integrity: sha512-C/rPwJcqnV8VDr2/VtcQnymSpcfEEgH1Jm6V0VmfXNZFv4Qzf1eCS8nsec0gipYgZB+cBBjfXw5dAk6pJ8ubpw==} + engines: {node: '>=16.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.7': + resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.7': + resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.24.7': + resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@es-joy/jsdoccomment@0.36.1': + resolution: {integrity: sha512-922xqFsTpHs6D0BUiG4toiyPOMc8/jafnWKxz1KWgS4XzKPy2qXf1Pe6UFuNSCQqt6tOuhAWXBNuuyUhJmw9Vg==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.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 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ewoudenberg/difflib@0.1.0': + resolution: {integrity: sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==} + + '@faker-js/faker@8.4.1': + resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.4.15': + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@liuli-util/fs-extra@0.1.0': + resolution: {integrity: sha512-eaAyDyMGT23QuRGbITVY3SOJff3G9ekAAyGqB9joAnTBmqvFN+9a1FazOdO70G6IUqgpKV451eBHYSRcOJ/FNQ==} + + '@mongodb-js/saslprep@1.1.7': + resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pagopa/eslint-config@3.0.0': + resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + + '@pagopa/interop-outbound-models@1.0.11-d': + resolution: {integrity: sha512-CAA22QItRcbxqSqJhKDvWCuHA+KjyzYeO0hYupJIinAcoQkao+MFnN+nxgI+z6GN23C7uhJIfX987LIra1YWdg==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@protobuf-ts/plugin-framework@2.9.4': + resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + + '@protobuf-ts/plugin@2.9.4': + resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} + hasBin: true + + '@protobuf-ts/protoc@2.9.4': + resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} + hasBin: true + + '@protobuf-ts/runtime-rpc@2.9.4': + resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + + '@protobuf-ts/runtime@2.9.4': + resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + + '@puppeteer/browsers@2.2.3': + resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==} + engines: {node: '>=18'} + hasBin: true + + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.5.17': + resolution: {integrity: sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.6': + resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.1.6': + resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.0.5': + resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@sinonjs/fake-timers@11.3.1': + resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} + + '@sinonjs/samsam@8.0.2': + resolution: {integrity: sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==} + + '@sinonjs/text-encoding@0.7.3': + resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} + + '@smithy/abort-controller@3.1.8': + resolution: {integrity: sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==} + engines: {node: '>=16.0.0'} + + '@smithy/chunked-blob-reader-native@3.0.1': + resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + + '@smithy/chunked-blob-reader@4.0.0': + resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + + '@smithy/config-resolver@3.0.12': + resolution: {integrity: sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.5.4': + resolution: {integrity: sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.1.3': + resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} + engines: {node: '>=16.0.0'} + + '@smithy/credential-provider-imds@3.2.7': + resolution: {integrity: sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-codec@3.1.9': + resolution: {integrity: sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==} + + '@smithy/eventstream-serde-browser@3.0.13': + resolution: {integrity: sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-config-resolver@3.0.10': + resolution: {integrity: sha512-K1M0x7P7qbBUKB0UWIL5KOcyi6zqV5mPJoL0/o01HPJr0CSq3A9FYuJC6e11EX6hR8QTIR++DBiGrYveOu6trw==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-node@3.0.12': + resolution: {integrity: sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==} + engines: {node: '>=16.0.0'} + + '@smithy/eventstream-serde-universal@3.0.12': + resolution: {integrity: sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==} + engines: {node: '>=16.0.0'} + + '@smithy/fetch-http-handler@3.2.9': + resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} + + '@smithy/fetch-http-handler@4.1.1': + resolution: {integrity: sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==} + + '@smithy/hash-blob-browser@3.1.9': + resolution: {integrity: sha512-wOu78omaUuW5DE+PVWXiRKWRZLecARyP3xcq5SmkXUw9+utgN8HnSnBfrjL2B/4ZxgqPjaAJQkC/+JHf1ITVaQ==} + + '@smithy/hash-node@3.0.10': + resolution: {integrity: sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-node@3.0.3': + resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} + engines: {node: '>=16.0.0'} + + '@smithy/hash-stream-node@3.1.9': + resolution: {integrity: sha512-3XfHBjSP3oDWxLmlxnt+F+FqXpL3WlXs+XXaB6bV9Wo8BBu87fK1dSEsyH7Z4ZHRmwZ4g9lFMdf08m9hoX1iRA==} + engines: {node: '>=16.0.0'} + + '@smithy/invalid-dependency@3.0.10': + resolution: {integrity: sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==} + + '@smithy/invalid-dependency@3.0.3': + resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@3.0.0': + resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} + engines: {node: '>=16.0.0'} + + '@smithy/md5-js@3.0.10': + resolution: {integrity: sha512-m3bv6dApflt3fS2Y1PyWPUtRP7iuBlvikEOGwu0HsCZ0vE7zcIX+dBoh3e+31/rddagw8nj92j0kJg2TfV+SJA==} + + '@smithy/middleware-content-length@3.0.12': + resolution: {integrity: sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.2.4': + resolution: {integrity: sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-retry@3.0.28': + resolution: {integrity: sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-serde@3.0.10': + resolution: {integrity: sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-stack@3.0.10': + resolution: {integrity: sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==} + engines: {node: '>=16.0.0'} + + '@smithy/node-config-provider@3.1.11': + resolution: {integrity: sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.3.1': + resolution: {integrity: sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@3.1.10': + resolution: {integrity: sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==} + engines: {node: '>=16.0.0'} + + '@smithy/property-provider@3.1.3': + resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.7': + resolution: {integrity: sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@3.0.10': + resolution: {integrity: sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-builder@3.0.3': + resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} + engines: {node: '>=16.0.0'} + + '@smithy/querystring-parser@3.0.10': + resolution: {integrity: sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==} + engines: {node: '>=16.0.0'} + + '@smithy/service-error-classification@3.0.10': + resolution: {integrity: sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==} + engines: {node: '>=16.0.0'} + + '@smithy/shared-ini-file-loader@3.1.11': + resolution: {integrity: sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@2.3.0': + resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} + engines: {node: '>=14.0.0'} + + '@smithy/signature-v4@3.1.2': + resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} + engines: {node: '>=16.0.0'} + + '@smithy/signature-v4@4.2.3': + resolution: {integrity: sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==} + engines: {node: '>=16.0.0'} + + '@smithy/smithy-client@3.4.5': + resolution: {integrity: sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==} + engines: {node: '>=16.0.0'} + + '@smithy/types@2.12.0': + resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} + engines: {node: '>=14.0.0'} + + '@smithy/types@3.7.1': + resolution: {integrity: sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==} + engines: {node: '>=16.0.0'} + + '@smithy/url-parser@3.0.10': + resolution: {integrity: sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==} + + '@smithy/util-base64@3.0.0': + resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-body-length-browser@3.0.0': + resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + + '@smithy/util-body-length-node@3.0.0': + resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@3.0.0': + resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-config-provider@3.0.0': + resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-defaults-mode-browser@3.0.28': + resolution: {integrity: sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-defaults-mode-node@3.0.28': + resolution: {integrity: sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==} + engines: {node: '>= 10.0.0'} + + '@smithy/util-endpoints@2.1.6': + resolution: {integrity: sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-hex-encoding@2.2.0': + resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} + engines: {node: '>=14.0.0'} + + '@smithy/util-hex-encoding@3.0.0': + resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} + engines: {node: '>=16.0.0'} + + '@smithy/util-middleware@2.2.0': + resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} + engines: {node: '>=14.0.0'} + + '@smithy/util-middleware@3.0.10': + resolution: {integrity: sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==} + engines: {node: '>=16.0.0'} + + '@smithy/util-retry@3.0.10': + resolution: {integrity: sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.3.1': + resolution: {integrity: sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==} + engines: {node: '>=16.0.0'} + + '@smithy/util-uri-escape@2.2.0': + resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-uri-escape@3.0.0': + resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} + engines: {node: '>=16.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@3.0.0': + resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} + engines: {node: '>=16.0.0'} + + '@smithy/util-waiter@3.1.9': + resolution: {integrity: sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==} + engines: {node: '>=16.0.0'} + + '@testcontainers/postgresql@10.9.0': + resolution: {integrity: sha512-Z3K/TFkl/PVE2v8A6yKqgF4pSFk9ilFG02yeGhPswUjmBlcig/rpVOjBQOkQ/yJCcQ/r2RrX3RR+7vr+UO4QlQ==} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@tsconfig/node-lts@20.1.3': + resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} + + '@tsconfig/strictest@2.0.5': + resolution: {integrity: sha512-ec4tjL2Rr0pkZ5hww65c+EEPYwxOi4Ryv+0MtjeaSQRJyq322Q27eOQiFbuNgw2hpL4hB1/W/HBGk3VKS43osg==} + + '@types/adm-zip@0.5.5': + resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} + + '@types/body-parser@1.19.5': + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + + '@types/buffers@0.1.31': + resolution: {integrity: sha512-wEZBb3o0Kh5RAj3V172vJCcxaCV8C2HJ7YLBBlG5Mwue0g4uRg5LWv8C6ap8MyFbXE6UbYEuvtHY7oTWAPeXEw==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + + '@types/dockerode@3.3.29': + resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/express-serve-static-core@4.19.5': + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + + '@types/express@4.17.21': + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/html2json@1.0.1': + resolution: {integrity: sha512-D+cq6HcgfMdrbIpXzxsmJ9mBz9VlyagqaIB9OZVHGrOeyWbwjTLWR2qUGh1w/VZLtG4wy8mp7v4EpJQ1dYqWzw==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/json-diff@1.0.3': + resolution: {integrity: sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@9.0.6': + resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + + '@types/lodash.isempty@4.4.9': + resolution: {integrity: sha512-DPSFfnT2JmZiAWNWOU8IRZws/Ha6zyGF5m06TydfsY+0dVoQqby2J61Na2QU4YtwiZ+moC6cJS6zWYBJq4wBVw==} + + '@types/lodash.isequal@4.5.8': + resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/multer@1.4.11': + resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + + '@types/node@18.19.39': + resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + + '@types/node@20.14.6': + resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + + '@types/node@20.4.9': + resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==} + + '@types/nodemailer@6.4.15': + resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} + + '@types/nodemailer@6.4.9': + resolution: {integrity: sha512-XYG8Gv+sHjaOtUpiuytahMy2mM3rectgroNbs6R3djZEKmPNiIJwe9KqOJBGzKKnNZNKvnuvmugBgpq3w/S0ig==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/send@0.17.4': + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/sinon@10.0.20': + resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} + + '@types/sinonjs__fake-timers@8.1.5': + resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + + '@types/ssh2-sftp-client@9.0.4': + resolution: {integrity: sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==} + + '@types/ssh2-streams@0.1.12': + resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.0': + resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@5.62.0': + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + 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 + + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + 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 + + '@typescript-eslint/scope-manager@5.62.0': + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/type-utils@5.62.0': + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@5.62.0': + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/typescript-estree@5.62.0': + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@5.62.0': + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@5.62.0': + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitest/expect@1.6.0': + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + + '@vitest/runner@1.6.0': + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + + '@vitest/snapshot@1.6.0': + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + + '@vitest/spy@1.6.0': + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + + '@vitest/utils@1.6.0': + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + + '@zodios/core@10.9.6': + resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} + peerDependencies: + axios: ^0.x || ^1.0.0 + zod: ^3.x + + '@zodios/express@10.6.1': + resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} + peerDependencies: + '@zodios/core': '>=10.4.4 <11.0.0' + express: 4.x + zod: ^3.x + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-jsx@2.0.1: + resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@2.7.0: + resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + adm-zip@0.5.15: + resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} + engines: {node: '>=12.0'} + + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + append-field@1.0.0: + resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} + + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.toreversed@1.1.2: + resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assert-options@0.8.1: + resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} + engines: {node: '>=10.0.0'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + aws-msk-iam-sasl-signer-js@1.0.0: + resolution: {integrity: sha512-L0Jk0k2XNHMSGipJ8rRdTq51KrH/gwrfZ39iKY9BWHGOAv7EygsG4qJC7lIRsbu5/ZHB886Z3WsOsFxqR2R4XQ==} + engines: {node: '>=14.x'} + + aws-sdk-client-mock@4.0.1: + resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==} + + axios-logger@2.8.1: + resolution: {integrity: sha512-Bbl7XRR/Rkxg2Owv/kRgAZ/0qf8kMPLc08LtiUcGCWV5RmoI7vHr+eee6SUc8jRi2nE5KWShziCVh35C1SBEaw==} + + axios@1.7.4: + resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + + b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.4.2: + resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} + + bare-fs@2.3.1: + resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} + + bare-os@2.4.0: + resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} + + bare-path@2.1.3: + resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} + + bare-stream@2.1.3: + resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.1: + resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buildcheck@0.0.6: + resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} + engines: {node: '>=10.0.0'} + + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001640: + resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} + + chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chromium-bidi@0.5.23: + resolution: {integrity: sha512-1o/gLU9wDqbN5nL2MtfjykjOuighGXc3/hnWueO1haiEoFgX8h5vbvcA4tgdQfjw1mkZ1OEF4x/+HVeqEX6NoA==} + peerDependencies: + devtools-protocol: '*' + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + 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==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colors@1.4.0: + resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} + engines: {node: '>=0.1.90'} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comment-parser@1.3.1: + resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} + engines: {node: '>= 12.0.0'} + + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + concat-stream@2.0.0: + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + connection-string@4.4.0: + resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} + engines: {node: '>=14'} + + console-assert@1.0.0: + resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + cpx2@7.0.1: + resolution: {integrity: sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==} + engines: {node: '>=18', npm: '>=10'} + hasBin: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + + create-eslint-index@1.0.0: + resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} + engines: {node: '>=4.0.0'} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + csv-generate@4.4.2: + resolution: {integrity: sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==} + + csv-parse@5.6.0: + resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + + csv-stringify@6.5.2: + resolution: {integrity: sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==} + + csv@6.3.2: + resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} + engines: {node: '>= 0.1.90'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns-tz@3.1.3: + resolution: {integrity: sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==} + peerDependencies: + date-fns: ^3.0.0 + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + dateformat@3.0.3: + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + + debounce@2.1.0: + resolution: {integrity: sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==} + engines: {node: '>=18'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge-ts@4.3.0: + resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} + engines: {node: '>=12.4.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + depseek@0.4.1: + resolution: {integrity: sha512-YYfPPajzH9s2qnEva411VJzCMWtArBTfluI9USiKQ+T6xBWFh3C7yPxhaa1KVgJa17v9aRKc+LcRhgxS5/9mOA==} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + devtools-protocol@0.0.1299070: + resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + docker-compose@0.24.8: + resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} + engines: {node: '>= 6.0.0'} + + docker-modem@3.0.8: + resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} + engines: {node: '>= 8.0'} + + dockerode@3.3.5: + resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} + engines: {node: '>= 8.0'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv-flow@4.1.0: + resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} + engines: {node: '>= 12.0.0'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + drange@1.1.1: + resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} + engines: {node: '>=4'} + + dreamopt@0.8.0: + resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} + engines: {node: '>=0.4.0'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.4.818: + resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-ast-utils@1.1.0: + resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} + engines: {node: '>=4'} + + eslint-config-prettier@8.10.0: + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.8.1: + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + 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 + + eslint-plugin-extra-rules@0.0.0-development: + resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} + engines: {node: '> 0.10.*'} + + eslint-plugin-fp@2.3.0: + resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} + engines: {node: '>=4.0.0'} + peerDependencies: + eslint: '>=3' + + eslint-plugin-functional@4.4.1: + 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 + + eslint-plugin-import@2.29.1: + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsdoc@39.9.1: + resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} + engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + eslint-plugin-prefer-arrow@1.2.3: + resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} + peerDependencies: + eslint: '>=2.0.0' + + eslint-plugin-prettier@4.2.1: + 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 + + eslint-plugin-react@7.34.3: + resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + + eslint-plugin-sonarjs@0.13.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 + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + 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} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@3.0.0-alpha-1: + resolution: {integrity: sha512-HIv6P6qCt3ciLWri1KrO7EPigKPetBZwfCf0o9TuAxRBEPoUUisCepsZqvM76xRfQf2sheO4BC5R/w3UKhwx4w==} + engines: {node: '>=0.10.0'} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eval-estree-expression@2.0.0: + resolution: {integrity: sha512-e1VweC8biANiuLBY1kZSva3PpaiqaL79S9RR9ql9ngidCYM6tsY20xrUBEJsN63A1yVSmO92chPaBpIGBmGsPg==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + express@4.19.2: + resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} + engines: {node: '>= 0.10.0'} + + express@4.20.0: + resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} + engines: {node: '>= 0.10.0'} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-xml-parser@4.2.5: + resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} + hasBin: true + + fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.2.0: + resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} + engines: {node: '>= 0.8'} + + find-index@0.1.1: + resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + + get-uri@6.0.3: + resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob2base@0.0.12: + resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} + engines: {node: '>= 0.10'} + + glob@10.4.3: + resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} + engines: {node: '>=18'} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + handlebars@4.7.7: + resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} + engines: {node: '>=0.4.7'} + hasBin: true + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + 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'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + heap@0.2.7: + resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} + + html2json@1.0.2: + resolution: {integrity: sha512-tCdVt82U+/D1GCXFIoN5VfCzx767065EZJ5B8nStQUGSXU9PQ4L/0kFvF2of3Qsoe9HGaJni+lxOkqakfw0zpA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + engines: {node: '>= 14'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + 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'} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + + jackspeak@3.4.1: + resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} + engines: {node: '>=18'} + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + jose@5.9.4: + resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + jsdoc-type-pratt-parser@3.1.0: + resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} + engines: {node: '>=12.0.0'} + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-diff@1.0.6: + resolution: {integrity: sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + just-extend@6.2.0: + resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jwks-rsa@3.1.0: + resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} + engines: {node: '>=14'} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + kafkajs@2.2.4: + resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} + engines: {node: '>=14.0.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + limiter@1.1.5: + resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isempty@4.4.0: + resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.zip@4.2.0: + resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + logform@2.6.0: + resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + engines: {node: '>= 12.0.0'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lru-cache@10.4.0: + resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} + engines: {node: '>=18'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru-memoizer@2.3.0: + resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@4.0.4: + resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} + engines: {node: '>=16'} + hasBin: true + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mnemonist@0.38.3: + resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.7.0: + resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multer@1.4.5-lts.1: + resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} + engines: {node: '>= 6.0.0'} + + nan@2.20.0: + resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + nise@5.1.9: + resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.14: + resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + + nodemailer@6.9.14: + resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==} + engines: {node: '>=6.0.0'} + + nodemailer@6.9.9: + resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} + engines: {node: '>=6.0.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.hasown@1.1.4: + resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + obliterator@1.6.1: + resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + openapi-zod-client@1.18.1: + resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} + hasBin: true + + openapi3-ts@3.1.0: + resolution: {integrity: sha512-1qKTvCCVoV0rkwUh1zq5o8QyghmwYPuhdvtjv1rFjuOnJToXhQyF8eGjNETQ8QmGjr9Jz/tkAKLITIl2s7dw3A==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@6.0.0: + resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} + engines: {node: '>=16'} + + pac-proxy-agent@7.0.2: + resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pastable@2.2.1: + resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} + engines: {node: '>=14.x'} + peerDependencies: + react: '>=17' + xstate: '>=4.32.1' + peerDependenciesMeta: + react: + optional: true + xstate: + optional: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-to-regexp@0.1.10: + resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + + path-to-regexp@0.1.7: + resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.6.4: + resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-minify@1.6.4: + resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} + engines: {node: '>=14.0.0'} + + pg-pool@3.6.2: + resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} + peerDependencies: + pg: '>=8.0' + + pg-promise@11.8.0: + resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} + engines: {node: '>=14.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.11.5: + resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + properties-reader@2.3.0: + resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} + engines: {node: '>=14'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.4.0: + resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + puppeteer-core@22.11.2: + resolution: {integrity: sha512-vQo+YDuePyvj+92Z9cdtxi/HalKf+k/R4tE80nGtQqJRNqU81eHaHkbVfnLszdaLlvwFF5tipnnSCzqWlEddtw==} + engines: {node: '>=18'} + + puppeteer@22.11.2: + resolution: {integrity: sha512-8fjdQSgW0sq7471ftca24J7sXK+jXZ7OW7Gx+NEBFNyXrcTiBfukEI46gNq6hiMhbLEDT30NeylK/1ZoPdlKSA==} + engines: {node: '>=18'} + hasBin: true + + qs@6.11.0: + resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} + engines: {node: '>=0.6'} + + qs@6.12.3: + resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} + engines: {node: '>=0.6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + + quote@0.4.0: + resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} + + randexp@0.5.3: + resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} + engines: {node: '>=4'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rate-limiter-flexible@5.0.3: + resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + redis@4.6.15: + resolution: {integrity: sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + req-all@0.1.0: + resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} + engines: {node: '>=4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + send@0.18.0: + resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} + engines: {node: '>= 0.8.0'} + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serve-static@1.15.0: + resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} + engines: {node: '>= 0.8.0'} + + serve-static@1.16.0: + resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} + engines: {node: '>= 0.8.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + sinon@16.1.3: + resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.4: + resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + engines: {node: '>= 14'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + + spex@3.3.0: + resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} + engines: {node: '>=10.0.0'} + + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2-sftp-client@9.1.0: + resolution: {integrity: sha512-Hzdr9OE6GxZjcmyM9tgBSIFVyrHAp9c6U2Y4yBkmYOHoQvZ7pIm27dmltvcmRfxcWiIcg8HBvG5iAikDf+ZuzQ==} + engines: {node: '>=10.24.1'} + + ssh2@1.15.0: + resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} + engines: {node: '>=10.16.0'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-transform@3.3.3: + resolution: {integrity: sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==} + + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + + streamx@2.18.0: + resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + + strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + + subarg@1.0.0: + resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tanu@0.1.13: + resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} + + tar-fs@2.0.1: + resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + + tar-fs@3.0.5: + resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + + tar-fs@3.0.6: + resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + testcontainers@10.9.0: + resolution: {integrity: sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==} + + text-decoder@1.1.1: + resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + + tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tmp@0.2.3: + resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} + engines: {node: '>=14.14'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-pattern@5.2.0: + resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + + ts-toolbelt@9.6.0: + resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + + tsc-esm-fix@2.20.27: + resolution: {integrity: sha512-bfoSY29XN4yRvXgfxc4rtKQPe9Xx02BahWSZ3D4GgBXIWSE+TJ/BXGSrpUIBkrsKIUQv2zA3qiwJVFnUV59Xdw==} + engines: {node: '>=16.0.0'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tsutils@3.21.0: + 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' + + tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + engines: {node: '>=18.0.0'} + hasBin: true + + turbo-darwin-64@2.2.3: + resolution: {integrity: sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.2.3: + resolution: {integrity: sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.2.3: + resolution: {integrity: sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.2.3: + resolution: {integrity: sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.2.3: + resolution: {integrity: sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.2.3: + resolution: {integrity: sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw==} + cpu: [arm64] + os: [win32] + + turbo@2.2.3: + resolution: {integrity: sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ==} + hasBin: true + + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typescript@3.9.10: + resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + uglify-js@3.18.0: + resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + unbzip2-stream@1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urlpattern-polyfill@10.0.0: + resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@1.6.0: + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + 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 + + 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 + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whence@2.0.1: + resolution: {integrity: sha512-VtcCE1Pe3BKofF/k+P5xcpuoqQ0f1NJY6TmdUw5kInl9/pEr1ZEFD9+ZOUicf52tvpTbhMS93aWXriu2IQYTTw==} + engines: {node: '>=14'} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + winston-transport@4.7.0: + resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + engines: {node: '>= 12.0.0'} + + winston@3.13.0: + resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + + zod-validation-error@3.3.0: + resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@anatine/zod-mock@3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8)': + dependencies: + '@faker-js/faker': 8.4.1 + randexp: 0.5.3 + zod: 3.23.8 + + '@apidevtools/json-schema-ref-parser@9.0.6': + dependencies: + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + js-yaml: 3.14.1 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3)': dependencies: '@apidevtools/json-schema-ref-parser': 9.0.6 '@apidevtools/openapi-schemas': 2.1.0 @@ -2940,25 +7469,19 @@ packages: call-me-maybe: 1.0.2 openapi-types: 12.1.3 - /@aws-crypto/crc32@5.2.0: - resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} - engines: {node: '>=16.0.0'} + '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 '@aws-sdk/types': 3.692.0 tslib: 2.6.3 - dev: false - /@aws-crypto/crc32c@5.2.0: - resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 '@aws-sdk/types': 3.692.0 tslib: 2.6.3 - dev: false - /@aws-crypto/sha1-browser@5.2.0: - resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 @@ -2966,10 +7489,8 @@ packages: '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@aws-crypto/sha256-browser@5.2.0: - resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + '@aws-crypto/sha256-browser@5.2.0': dependencies: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 @@ -2979,52 +7500,42 @@ packages: '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - /@aws-crypto/sha256-js@4.0.0: - resolution: {integrity: sha512-MHGJyjE7TX9aaqXj7zk2ppnFUOhaDs5sP+HtNS0evOxn72c+5njUmyJmpGd7TfyoDznZlHMmdo/xGUdu2NIjNQ==} + '@aws-crypto/sha256-js@4.0.0': dependencies: '@aws-crypto/util': 4.0.0 '@aws-sdk/types': 3.609.0 tslib: 1.14.1 - dev: false - /@aws-crypto/sha256-js@5.2.0: - resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} - engines: {node: '>=16.0.0'} + '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 '@aws-sdk/types': 3.692.0 tslib: 2.6.3 - /@aws-crypto/supports-web-crypto@5.2.0: - resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + '@aws-crypto/supports-web-crypto@5.2.0': dependencies: tslib: 2.6.3 - /@aws-crypto/util@4.0.0: - resolution: {integrity: sha512-2EnmPy2gsFZ6m8bwUQN4jq+IyXV3quHAcwPOS6ZA3k+geujiqI8aRokO2kFJe+idJ/P3v4qWI186rVMo0+zLDQ==} + '@aws-crypto/util@4.0.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 - dev: false - /@aws-crypto/util@5.2.0: - resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + '@aws-crypto/util@5.2.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - /@aws-sdk/client-cognito-identity@3.609.0: - resolution: {integrity: sha512-3kDTpia1iN/accayoH3MbZRbDvX2tzrKrBTU7wNNoazVrh+gOMS8KCOWrOB72F0V299l4FsfQhnl9BDMVrc1iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-cognito-identity@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -3062,18 +7573,15 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-dynamodb@3.693.0: - resolution: {integrity: sha512-EmgFoE/wAxiOq/sfO/VFGlmvfq0FexUO4IMURr3deIpU/AuCsuU87HJH/UodFdKu88ykNZxMfHHku6o6BV2dAA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-dynamodb@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-endpoint-discovery': 3.693.0 '@aws-sdk/middleware-host-header': 3.693.0 '@aws-sdk/middleware-logger': 3.693.0 @@ -3116,16 +7624,14 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-kms@3.693.0: - resolution: {integrity: sha512-9APrRDbScYqfCG89Trruf9+Bx39wlXQdwPEhluycpCF+bHYiTKdIXc4uix6hjbXzK0NKbgitus8bbMvsHX+Oxg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-kms@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-host-header': 3.693.0 '@aws-sdk/middleware-logger': 3.693.0 '@aws-sdk/middleware-recursion-detection': 3.693.0 @@ -3163,11 +7669,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-s3@3.693.0: - resolution: {integrity: sha512-vgGI2e0Q6pzyhqfrSysi+sk/i+Nl+lMon67oqj/57RcCw9daL1/inpS+ADuwHpiPWkrg+U0bOXnmHjkLeTslJg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-s3@3.693.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 @@ -3175,7 +7678,7 @@ packages: '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-bucket-endpoint': 3.693.0 '@aws-sdk/middleware-expect-continue': 3.693.0 '@aws-sdk/middleware-flexible-checksums': 3.693.0 @@ -3229,18 +7732,15 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sesv2@3.693.0: - resolution: {integrity: sha512-5lQUWTxNsd933o1TvAsa564BeMv8IbhxHIgssCxf3ibnqUgI7S/R6Bk62KqlxjxcgWDXabQWQskejZ8cpupYqA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sesv2@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-host-header': 3.693.0 '@aws-sdk/middleware-logger': 3.693.0 '@aws-sdk/middleware-recursion-detection': 3.693.0 @@ -3279,16 +7779,14 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sqs@3.693.0: - resolution: {integrity: sha512-Nfmo6bgad26PP1B7MZP9kpxOw874cVEvJHXBXwkSLg6ZPAeU2cnvTEE++omHkzLeeB9MbpmvhQtdaUn4c7DYZQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sqs@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-host-header': 3.693.0 '@aws-sdk/middleware-logger': 3.693.0 '@aws-sdk/middleware-recursion-detection': 3.693.0 @@ -3328,19 +7826,14 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-0bNPAyPdkWkS9EGB2A9BZDkBNrnVCBzk5lYRezoT4K3/gi9w1DTYH5tuRdwaTZdxW19U1mq7CV0YJJARKO1L9Q==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -3378,19 +7871,14 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0): - resolution: {integrity: sha512-UEDbYlYtK/e86OOMyFR4zEPyenIxDzO2DRdz3fwVW7RzZ94wfmSwBh/8skzPTuY1G7sI064cjHW0b0QG01Sdtg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.693.0 + '@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-host-header': 3.693.0 '@aws-sdk/middleware-logger': 3.693.0 '@aws-sdk/middleware-recursion-detection': 3.693.0 @@ -3429,9 +7917,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sso@3.609.0: - resolution: {integrity: sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3473,11 +7959,8 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sso@3.693.0: - resolution: {integrity: sha512-QEynrBC26x6TG9ZMzApR/kZ3lmt4lEIs2D+cHuDxt6fDGzahBUsQFBwJqhizzsM97JJI5YvmJhmihoYjdSSaXA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 @@ -3520,15 +8003,13 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/client-sts@3.609.0: - resolution: {integrity: sha512-A0B3sDKFoFlGo8RYRjDBWHXpbgirer2bZBkCIzhSPHc1vOFHt/m2NcUoE2xnBKXJFrptL1xDkvo1P+XYp/BfcQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.609.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/core': 3.609.0 - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/middleware-host-header': 3.609.0 '@aws-sdk/middleware-logger': 3.609.0 '@aws-sdk/middleware-recursion-detection': 3.609.0 @@ -3566,17 +8047,14 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/client-sts@3.693.0: - resolution: {integrity: sha512-4S2y7VEtvdnjJX4JPl4kDQlslxXEZFnC50/UXVUYSt/AMc5A/GgspFNA5FVz4E3Gwpfobbf23hR2NBF8AGvYoQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.693.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/core': 3.693.0 - '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-node': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/middleware-host-header': 3.693.0 '@aws-sdk/middleware-logger': 3.693.0 '@aws-sdk/middleware-recursion-detection': 3.693.0 @@ -3615,9 +8093,7 @@ packages: transitivePeerDependencies: - aws-crt - /@aws-sdk/core@3.609.0: - resolution: {integrity: sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.609.0': dependencies: '@smithy/core': 2.5.4 '@smithy/protocol-http': 4.1.7 @@ -3626,11 +8102,8 @@ packages: '@smithy/types': 3.7.1 fast-xml-parser: 4.2.5 tslib: 2.6.3 - dev: false - /@aws-sdk/core@3.693.0: - resolution: {integrity: sha512-v6Z/kWmLFqRLDPEwl9hJGhtTgIFHjZugSfF1Yqffdxf4n1AWgtHS7qSegakuMyN5pP4K2tvUD8qHJ+gGe2Bw2A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/core': 2.5.4 @@ -3644,9 +8117,7 @@ packages: fast-xml-parser: 4.4.1 tslib: 2.6.3 - /@aws-sdk/credential-provider-cognito-identity@3.609.0: - resolution: {integrity: sha512-BqrpAXRr64dQ/uZsRB2wViGKTkVRlfp8Q+Zd7Bc8Ikk+YXjPtl+IyWXKtdKQ3LBO255KwAcPmra5oFC+2R1GOQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-cognito-identity@3.609.0': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/types': 3.609.0 @@ -3655,21 +8126,15 @@ packages: tslib: 2.6.3 transitivePeerDependencies: - aws-crt - dev: false - /@aws-sdk/credential-provider-env@3.609.0: - resolution: {integrity: sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.10 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-env@3.693.0: - resolution: {integrity: sha512-hMUZaRSF7+iBKZfBHNLihFs9zvpM1CB8MBOTnTp5NGCVkRYF3SB2LH+Kcippe0ats4qCyB1eEoyQX99rERp2iQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.693.0': dependencies: '@aws-sdk/core': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -3677,9 +8142,7 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/credential-provider-http@3.609.0: - resolution: {integrity: sha512-GQQfB9Mk4XUZwaPsk4V3w8MqleS6ApkZKVQn3vTLAKa8Y7B2Imcpe5zWbKYjDd8MPpMWjHcBGFTVlDRFP4zwSQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.9 @@ -3690,11 +8153,8 @@ packages: '@smithy/types': 3.7.1 '@smithy/util-stream': 3.3.1 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-http@3.693.0: - resolution: {integrity: sha512-sL8MvwNJU7ZpD7/d2VVb3by1GknIJUxzTIgYtVkDVA/ojo+KRQSSHxcj0EWWXF5DTSh2Tm+LrEug3y1ZyKHsDA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.693.0': dependencies: '@aws-sdk/core': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -3707,17 +8167,13 @@ packages: '@smithy/util-stream': 3.3.1 tslib: 2.6.3 - /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.7 @@ -3728,19 +8184,14 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-hwaBfXuBTv6/eAdEsDfGcteYUW6Km7lvvubbxEdxIuJNF3vswR7RMGIXaEC37hhPkTTgd3H0TONammhwZIfkog==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.7 @@ -3751,20 +8202,34 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-ini@3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0): - resolution: {integrity: sha512-kvaa4mXhCCOuW7UQnBhYqYfgWmwy7WSBSDClutwSLPZvgrhYj2l16SD2lN4IfYdxARYMJJ1lFYp3/jJG/9Yk4Q==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.693.0 + '@aws-sdk/credential-provider-ini@3.609.0(@aws-sdk/client-sts@3.609.0)': + dependencies: + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0 + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + optional: true + + '@aws-sdk/credential-provider-ini@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0)': dependencies: '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 '@aws-sdk/credential-provider-env': 3.693.0 '@aws-sdk/credential-provider-http': 3.693.0 '@aws-sdk/credential-provider-process': 3.693.0 - '@aws-sdk/credential-provider-sso': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/credential-provider-sso': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/types': 3.692.0 '@smithy/credential-provider-imds': 3.2.7 @@ -3776,15 +8241,13 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.7 @@ -3796,17 +8259,14 @@ packages: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - dev: false - /@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-4J8/JRuqfxJDGD9jTHVCBxCvYt7/Vgj2Stlhj930mrjFPO/yRw8ilAAZxBWe0JHPX3QwepCmh4ErZe53F5ysxQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.2.7 @@ -3818,17 +8278,34 @@ packages: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - dev: false - /@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0): - resolution: {integrity: sha512-42WMsBjTNnjYxYuM3qD/Nq+8b7UdMopUq5OduMDxoM3mFTV6PXMMnfI4Z1TNnR4tYRvPXAnuNltF6xmjKbSJRA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.609.0(@aws-sdk/client-sts@3.609.0)': + dependencies: + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0 + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.2.7 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - '@aws-sdk/client-sts' + - aws-crt + optional: true + + '@aws-sdk/credential-provider-node@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0)': dependencies: '@aws-sdk/credential-provider-env': 3.693.0 '@aws-sdk/credential-provider-http': 3.693.0 - '@aws-sdk/credential-provider-ini': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.693.0) + '@aws-sdk/credential-provider-ini': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.693.0) '@aws-sdk/credential-provider-process': 3.693.0 - '@aws-sdk/credential-provider-sso': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/credential-provider-sso': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/types': 3.692.0 '@smithy/credential-provider-imds': 3.2.7 @@ -3841,20 +8318,15 @@ packages: - '@aws-sdk/client-sts' - aws-crt - /@aws-sdk/credential-provider-process@3.609.0: - resolution: {integrity: sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.10 '@smithy/shared-ini-file-loader': 3.1.11 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-process@3.693.0: - resolution: {integrity: sha512-cvxQkrTWHHjeHrPlj7EWXPnFSq8x7vMx+Zn1oTsMpCY445N9KuzjfJTkmNGwU2GT6rSZI9/0MM02aQvl5bBBTQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.693.0': dependencies: '@aws-sdk/core': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -3863,12 +8335,10 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): - resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.609.0': dependencies: '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0) + '@aws-sdk/token-providers': 3.609.0 '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.10 '@smithy/shared-ini-file-loader': 3.1.11 @@ -3877,14 +8347,12 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false + optional: true - /@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.693.0): - resolution: {integrity: sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso': 3.609.0 - '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.10 '@smithy/shared-ini-file-loader': 3.1.11 @@ -3893,15 +8361,25 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /@aws-sdk/credential-provider-sso@3.693.0(@aws-sdk/client-sso-oidc@3.693.0): - resolution: {integrity: sha512-479UlJxY+BFjj3pJFYUNC0DCMrykuG7wBAXfsvZqQxKUa83DnH5Q1ID/N2hZLkxjGd4ZW0AC3lTOMxFelGzzpQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': + dependencies: + '@aws-sdk/client-sso': 3.609.0 + '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/credential-provider-sso@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: '@aws-sdk/client-sso': 3.693.0 '@aws-sdk/core': 3.693.0 - '@aws-sdk/token-providers': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/token-providers': 3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/types': 3.692.0 '@smithy/property-provider': 3.1.10 '@smithy/shared-ini-file-loader': 3.1.11 @@ -3911,24 +8389,15 @@ packages: - '@aws-sdk/client-sso-oidc' - aws-crt - /@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0): - resolution: {integrity: sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.609.0 + '@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.609.0)': dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/types': 3.609.0 '@smithy/property-provider': 3.1.10 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/credential-provider-web-identity@3.693.0(@aws-sdk/client-sts@3.693.0): - resolution: {integrity: sha512-8LB210Pr6VeCiSb2hIra+sAH4KUBLyGaN50axHtIgufVK8jbKIctTZcVY5TO9Se+1107TsruzeXS7VeqVdJfFA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sts': ^3.693.0 + '@aws-sdk/credential-provider-web-identity@3.693.0(@aws-sdk/client-sts@3.693.0)': dependencies: '@aws-sdk/client-sts': 3.693.0 '@aws-sdk/core': 3.693.0 @@ -3937,9 +8406,7 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0): - resolution: {integrity: sha512-bJKMY4QwRVderh8R2s9kukoZhuNZew/xzwPa9DRRFVOIsznsS0faAdmAAFrKb8e06YyQq6DiZP0BfFyVHAXE2A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-providers@3.609.0': dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/client-sso': 3.609.0 @@ -3947,10 +8414,10 @@ packages: '@aws-sdk/credential-provider-cognito-identity': 3.609.0 '@aws-sdk/credential-provider-env': 3.609.0 '@aws-sdk/credential-provider-http': 3.609.0 - '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.609.0) - '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0)(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/credential-provider-process': 3.609.0 - '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/credential-provider-sso': 3.609.0 '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 '@smithy/credential-provider-imds': 3.1.3 @@ -3960,18 +8427,36 @@ packages: transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false + optional: true - /@aws-sdk/endpoint-cache@3.693.0: - resolution: {integrity: sha512-/zK0ZZncBf5FbTfo8rJMcQIXXk4Ibhe5zEMiwFNivVPR2uNC0+oqfwXz7vjxwY0t6BPE3Bs4h9uFEz4xuGCY6w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': + dependencies: + '@aws-sdk/client-cognito-identity': 3.609.0 + '@aws-sdk/client-sso': 3.609.0 + '@aws-sdk/client-sts': 3.609.0 + '@aws-sdk/credential-provider-cognito-identity': 3.609.0 + '@aws-sdk/credential-provider-env': 3.609.0 + '@aws-sdk/credential-provider-http': 3.609.0 + '@aws-sdk/credential-provider-ini': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-node': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/credential-provider-process': 3.609.0 + '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) + '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) + '@aws-sdk/types': 3.609.0 + '@smithy/credential-provider-imds': 3.1.3 + '@smithy/property-provider': 3.1.3 + '@smithy/types': 3.7.1 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sso-oidc' + - aws-crt + + '@aws-sdk/endpoint-cache@3.693.0': dependencies: mnemonist: 0.38.3 tslib: 2.6.3 - /@aws-sdk/middleware-bucket-endpoint@3.693.0: - resolution: {integrity: sha512-cPIa+lxMYiFRHtxKfNIVSFGO6LSgZCk42pu3d7KGwD6hu6vXRD5B2/DD3rPcEH1zgl2j0Kx1oGAV7SRXKHSFag==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@aws-sdk/util-arn-parser': 3.693.0 @@ -3980,11 +8465,8 @@ packages: '@smithy/types': 3.7.1 '@smithy/util-config-provider': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-endpoint-discovery@3.693.0: - resolution: {integrity: sha512-OG8WM8OzYuAt3Ueb8TZoBgA+vqNgPaXksHhiy8SFTQxNamSMMRvKrDSBbdUuV96mq0lcJq1mFgJ4oRXJ1HPh6A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-endpoint-discovery@3.693.0': dependencies: '@aws-sdk/endpoint-cache': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -3993,19 +8475,14 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/middleware-expect-continue@3.693.0: - resolution: {integrity: sha512-MuK/gsJWpHz6Tv0CqTCS+QNOxLa2RfPh1biVCu/uO3l7kA0TjQ/C+tfgKvLXeH103tuDrOVINK+bt2ENmI3SWg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-expect-continue@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-flexible-checksums@3.693.0: - resolution: {integrity: sha512-xkS6zjuE11ob93H9t65kHzphXcUMnN2SmIm2wycUPg+hi8Q6DJA6U2p//6oXkrr9oHy1QvwtllRd7SAd63sFKQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.693.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 @@ -4020,75 +8497,54 @@ packages: '@smithy/util-stream': 3.3.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.609.0: - resolution: {integrity: sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-host-header@3.693.0: - resolution: {integrity: sha512-BCki6sAZ5jYwIN/t3ElCiwerHad69ipHwPsDCxJQyeiOnJ8HG+lEpnVIfrnI8A0fLQNSF3Gtx6ahfBpKiv1Oug==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/middleware-location-constraint@3.693.0: - resolution: {integrity: sha512-eDAExTZ9uNIP7vs2JCVCOuWJauGueisBSn+Ovt7UvvuEUp6KOIJqn8oFxWmyUQu2GvbG4OcaTLgbqD95YHTB0Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-location-constraint@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.609.0: - resolution: {integrity: sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-logger@3.693.0: - resolution: {integrity: sha512-dXnXDPr+wIiJ1TLADACI1g9pkSB21KkMIko2u4CJ2JCBoxi5IqeTnVoa6YcC8GdFNVRl+PorZ3Zqfmf1EOTC6w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/middleware-recursion-detection@3.609.0: - resolution: {integrity: sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-recursion-detection@3.693.0: - resolution: {integrity: sha512-0LDmM+VxXp0u3rG0xQRWD/q6Ubi7G8I44tBPahevD5CaiDZTkmNTrVUf0VEJgVe0iCKBppACMBDkLB0/ETqkFw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/middleware-sdk-s3@3.693.0: - resolution: {integrity: sha512-5A++RBjJ3guyq5pbYs+Oq5hMlA8CK2OWaHx09cxVfhHWl/RoaY8DXrft4gnhoUEBrrubyMw7r9j7RIMLvS58kg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.693.0': dependencies: '@aws-sdk/core': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -4104,11 +8560,8 @@ packages: '@smithy/util-stream': 3.3.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-sdk-sqs@3.693.0: - resolution: {integrity: sha512-txpz7gKX6b8NE+VdIy3UQzEXCNM0ubpuaEmkr1yReNh0x8hqslxtQyjhn6tqKvAABQ/VnKSJbbqMzsi710fR4Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-sqs@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/smithy-client': 3.4.5 @@ -4116,31 +8569,22 @@ packages: '@smithy/util-hex-encoding': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-ssec@3.693.0: - resolution: {integrity: sha512-Ro5vzI7SRgEeuoMk3fKqFjGv6mG4c7VsSCDwnkiasmafQFBTPvUIpgmu2FXMHqW/OthvoiOzpSrlJ9Bwlx2f8A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-ssec@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.609.0: - resolution: {integrity: sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.609.0 '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/middleware-user-agent@3.693.0: - resolution: {integrity: sha512-/KUq/KEpFFbQmNmpp7SpAtFAdViquDfD2W0QcG07zYBfz9MwE2ig48ALynXm5sMpRmnG7sJXjdvPtTsSVPfkiw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.693.0': dependencies: '@aws-sdk/core': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -4150,9 +8594,7 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/region-config-resolver@3.609.0: - resolution: {integrity: sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.11 @@ -4160,11 +8602,8 @@ packages: '@smithy/util-config-provider': 3.0.0 '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 - dev: false - /@aws-sdk/region-config-resolver@3.693.0: - resolution: {integrity: sha512-YLUkMsUY0GLW/nfwlZ69cy1u07EZRmsv8Z9m0qW317/EZaVx59hcvmcvb+W4bFqj5E8YImTjoGfE4cZ0F9mkyw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/node-config-provider': 3.1.11 @@ -4173,9 +8612,7 @@ packages: '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 - /@aws-sdk/s3-request-presigner@3.693.0: - resolution: {integrity: sha512-I/TCM43kZn1xb+EWMAjkcisDVrq3mYsu0ZFP81J9K/PM6n3s9bK04jaY56c3pCl6btigIOHhreutYSRRBJsCDw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/s3-request-presigner@3.693.0': dependencies: '@aws-sdk/signature-v4-multi-region': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -4185,11 +8622,8 @@ packages: '@smithy/smithy-client': 3.4.5 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/signature-v4-multi-region@3.693.0: - resolution: {integrity: sha512-s7zbbsoVIriTR4ZGaateKuTqz6ddpazAyHvjk7I9kd+NvGNPiuAI18UdbuiiRI6K5HuYKf1ah6mKWFGPG15/kQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.693.0': dependencies: '@aws-sdk/middleware-sdk-s3': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -4197,13 +8631,17 @@ packages: '@smithy/signature-v4': 4.2.3 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0): - resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.609.0 + '@aws-sdk/token-providers@3.609.0': + dependencies: + '@aws-sdk/types': 3.609.0 + '@smithy/property-provider': 3.1.10 + '@smithy/shared-ini-file-loader': 3.1.11 + '@smithy/types': 3.7.1 + tslib: 2.6.3 + optional: true + + '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 @@ -4211,13 +8649,8 @@ packages: '@smithy/shared-ini-file-loader': 3.1.11 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0): - resolution: {integrity: sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.609.0 + '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/types': 3.609.0 @@ -4225,13 +8658,8 @@ packages: '@smithy/shared-ini-file-loader': 3.1.11 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/token-providers@3.693.0(@aws-sdk/client-sso-oidc@3.693.0): - resolution: {integrity: sha512-nDBTJMk1l/YmFULGfRbToOA2wjf+FkQT4dMgYCv+V9uSYsMzQj8A7Tha2dz9yv4vnQgYaEiErQ8d7HVyXcVEoA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.693.0 + '@aws-sdk/token-providers@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/types': 3.692.0 @@ -4240,122 +8668,79 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/types@3.609.0: - resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.609.0': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/types@3.692.0: - resolution: {integrity: sha512-RpNvzD7zMEhiKgmlxGzyXaEcg2khvM7wd5sSHVapOcrde1awQSOMGI4zKBQ+wy5TnDfrm170ROz/ERLYtrjPZA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.692.0': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/util-arn-parser@3.693.0: - resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.693.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/util-dynamodb@3.693.0(@aws-sdk/client-dynamodb@3.693.0): - resolution: {integrity: sha512-lwJnlQndVS7cGN1UmtG4s6IdVW3U/WheJ14Xc/mUeAH3vga7GTY3hGi+Jp1TbnaYQkad589tU+NQ5KUW7V8ZjA==} - engines: {node: '>=16.0.0'} - peerDependencies: - '@aws-sdk/client-dynamodb': ^3.693.0 + '@aws-sdk/util-dynamodb@3.693.0(@aws-sdk/client-dynamodb@3.693.0)': dependencies: '@aws-sdk/client-dynamodb': 3.693.0 tslib: 2.6.3 - /@aws-sdk/util-endpoints@3.609.0: - resolution: {integrity: sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.7.1 '@smithy/util-endpoints': 2.1.6 tslib: 2.6.3 - dev: false - /@aws-sdk/util-endpoints@3.693.0: - resolution: {integrity: sha512-eo4F6DRQ/kxS3gxJpLRv+aDNy76DxQJL5B3DPzpr9Vkq0ygVoi4GT5oIZLVaAVIJmi6k5qq9dLsYZfWLUxJJSg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/types': 3.7.1 '@smithy/util-endpoints': 2.1.6 tslib: 2.6.3 - /@aws-sdk/util-format-url@3.609.0: - resolution: {integrity: sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-format-url@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/querystring-builder': 3.0.3 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/util-format-url@3.693.0: - resolution: {integrity: sha512-0O4fSq45GOwC89Os0f92z9kK1AV22+W980O+v+GkMLUkRG7/nsIJkq1LKiIPV+sbC+KC/HmW4yThxFzHO7GDxA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-format-url@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/querystring-builder': 3.0.10 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/util-locate-window@3.568.0: - resolution: {integrity: sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-locate-window@3.568.0': dependencies: tslib: 2.6.3 - /@aws-sdk/util-user-agent-browser@3.609.0: - resolution: {integrity: sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==} + '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/types': 3.7.1 bowser: 2.11.0 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-browser@3.693.0: - resolution: {integrity: sha512-6EUfuKOujtddy18OLJUaXfKBgs+UcbZ6N/3QV4iOkubCUdeM1maIqs++B9bhCbWeaeF5ORizJw5FTwnyNjE/mw==} + '@aws-sdk/util-user-agent-browser@3.693.0': dependencies: '@aws-sdk/types': 3.692.0 '@smithy/types': 3.7.1 bowser: 2.11.0 tslib: 2.6.3 - /@aws-sdk/util-user-agent-node@3.609.0: - resolution: {integrity: sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 '@smithy/node-config-provider': 3.1.11 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@aws-sdk/util-user-agent-node@3.693.0: - resolution: {integrity: sha512-td0OVX8m5ZKiXtecIDuzY3Y3UZIzvxEr57Hp21NOwieqKCG2UeyQWWeGPv0FQaU7dpTkvFmVNI+tx9iB8V/Nhg==} - engines: {node: '>=16.0.0'} - peerDependencies: - aws-crt: '>=1.0.0' - peerDependenciesMeta: - aws-crt: - optional: true + '@aws-sdk/util-user-agent-node@3.693.0': dependencies: '@aws-sdk/middleware-user-agent': 3.693.0 '@aws-sdk/types': 3.692.0 @@ -4363,34 +8748,23 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@aws-sdk/util-utf8-browser@3.259.0: - resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + '@aws-sdk/util-utf8-browser@3.259.0': dependencies: tslib: 2.6.3 - dev: false - /@aws-sdk/xml-builder@3.693.0: - resolution: {integrity: sha512-C/rPwJcqnV8VDr2/VtcQnymSpcfEEgH1Jm6V0VmfXNZFv4Qzf1eCS8nsec0gipYgZB+cBBjfXw5dAk6pJ8ubpw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/xml-builder@3.693.0': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@babel/code-frame@7.24.7: - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - /@babel/compat-data@7.24.7: - resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} - engines: {node: '>=6.9.0'} + '@babel/compat-data@7.24.7': {} - /@babel/core@7.24.7: - resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} - engines: {node: '>=6.9.0'} + '@babel/core@7.24.7': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.7 @@ -4410,18 +8784,14 @@ packages: transitivePeerDependencies: - supports-color - /@babel/generator@7.24.7: - resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} - engines: {node: '>=6.9.0'} + '@babel/generator@7.24.7': dependencies: '@babel/types': 7.24.7 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - /@babel/helper-compilation-targets@7.24.7: - resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} - engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.24.7': dependencies: '@babel/compat-data': 7.24.7 '@babel/helper-validator-option': 7.24.7 @@ -4429,39 +8799,27 @@ packages: lru-cache: 5.1.1 semver: 6.3.1 - /@babel/helper-environment-visitor@7.24.7: - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-environment-visitor@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-function-name@7.24.7: - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} + '@babel/helper-function-name@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - /@babel/helper-hoist-variables@7.24.7: - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-hoist-variables@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-module-imports@7.24.7: - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - /@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7): - resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': dependencies: '@babel/core': 7.24.7 '@babel/helper-environment-visitor': 7.24.7 @@ -4472,67 +8830,46 @@ packages: transitivePeerDependencies: - supports-color - /@babel/helper-simple-access@7.24.7: - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} + '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 transitivePeerDependencies: - supports-color - /@babel/helper-split-export-declaration@7.24.7: - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} + '@babel/helper-split-export-declaration@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/helper-string-parser@7.24.7: - resolution: {integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==} - engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.7': {} - /@babel/helper-validator-identifier@7.24.7: - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': {} - /@babel/helper-validator-option@7.24.7: - resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.24.7': {} - /@babel/helpers@7.24.7: - resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} - engines: {node: '>=6.9.0'} + '@babel/helpers@7.24.7': dependencies: '@babel/template': 7.24.7 '@babel/types': 7.24.7 - /@babel/highlight@7.24.7: - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - /@babel/parser@7.24.7: - resolution: {integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==} - engines: {node: '>=6.0.0'} - hasBin: true + '@babel/parser@7.24.7': dependencies: '@babel/types': 7.24.7 - /@babel/template@7.24.7: - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} - engines: {node: '>=6.9.0'} + '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - /@babel/traverse@7.24.7: - resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} - engines: {node: '>=6.9.0'} + '@babel/traverse@7.24.7': dependencies: '@babel/code-frame': 7.24.7 '@babel/generator': 7.24.7 @@ -4547,488 +8884,177 @@ packages: transitivePeerDependencies: - supports-color - /@babel/types@7.24.7: - resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} - engines: {node: '>=6.9.0'} + '@babel/types@7.24.7': dependencies: '@babel/helper-string-parser': 7.24.7 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - /@balena/dockerignore@1.0.2: - resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} - dev: true - - /@colors/colors@1.6.0: - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - 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 + '@balena/dockerignore@1.0.2': {} - /@dabh/diagnostics@2.0.3: - resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@colors/colors@1.6.0': {} + + '@dabh/diagnostics@2.0.3': 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} + '@es-joy/jsdoccomment@0.36.1': dependencies: comment-parser: 1.3.1 esquery: 1.5.0 jsdoc-type-pratt-parser: 3.1.0 - dev: true - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.21.5': optional: true - /@esbuild/aix-ppc64@0.23.1: - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.23.1': optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.21.5': optional: true - /@esbuild/android-arm64@0.23.1: - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.23.1': optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.21.5': optional: true - /@esbuild/android-arm@0.23.1: - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.23.1': optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-x64@0.21.5': optional: true - /@esbuild/android-x64@0.23.1: - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-x64@0.23.1': optional: true - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.21.5': optional: true - /@esbuild/darwin-arm64@0.23.1: - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.23.1': optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.21.5': optional: true - /@esbuild/darwin-x64@0.23.1: - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.23.1': optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.21.5': optional: true - /@esbuild/freebsd-arm64@0.23.1: - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.23.1': optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.21.5': optional: true - /@esbuild/freebsd-x64@0.23.1: - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.23.1': optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.21.5': optional: true - /@esbuild/linux-arm64@0.23.1: - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.23.1': optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.21.5': optional: true - /@esbuild/linux-arm@0.23.1: - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.23.1': optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.21.5': optional: true - /@esbuild/linux-ia32@0.23.1: - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.23.1': optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.21.5': optional: true - /@esbuild/linux-loong64@0.23.1: - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.23.1': optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.21.5': optional: true - /@esbuild/linux-mips64el@0.23.1: - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.23.1': optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.21.5': optional: true - /@esbuild/linux-ppc64@0.23.1: - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.23.1': optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.21.5': optional: true - /@esbuild/linux-riscv64@0.23.1: - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.23.1': optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.21.5': optional: true - /@esbuild/linux-s390x@0.23.1: - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.23.1': optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.21.5': optional: true - /@esbuild/linux-x64@0.23.1: - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.23.1': optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.21.5': optional: true - /@esbuild/netbsd-x64@0.23.1: - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.23.1': optional: true - /@esbuild/openbsd-arm64@0.23.1: - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-arm64@0.23.1': optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.21.5': optional: true - /@esbuild/openbsd-x64@0.23.1: - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.23.1': optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.21.5': optional: true - /@esbuild/sunos-x64@0.23.1: - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.23.1': optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.21.5': optional: true - /@esbuild/win32-arm64@0.23.1: - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.23.1': optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.21.5': optional: true - /@esbuild/win32-ia32@0.23.1: - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.23.1': optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-x64@0.21.5': optional: true - /@esbuild/win32-x64@0.23.1: - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-x64@0.23.1': optional: 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} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - dev: true - /@eslint-community/regexpp@4.11.0: - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true + '@eslint-community/regexpp@4.11.0': {} - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 debug: 4.3.5 @@ -5041,147 +9067,93 @@ packages: strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - 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} - dev: true + '@eslint/js@8.57.0': {} - /@ewoudenberg/difflib@0.1.0: - resolution: {integrity: sha512-OU5P5mJyD3OoWYMWY+yIgwvgNS9cFAU10f+DDuvtogcWQOoJIsQ4Hy2McSfUfhKjq8L0FuWVb4Rt7kgA+XK86A==} + '@ewoudenberg/difflib@0.1.0': dependencies: heap: 0.2.7 - dev: false - /@faker-js/faker@8.4.1: - resolution: {integrity: sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13'} - dev: true + '@faker-js/faker@8.4.1': {} - /@humanwhocodes/config-array@0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.5 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/module-importer@1.0.1': {} - /@humanwhocodes/object-schema@2.0.3: - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - dev: true + '@humanwhocodes/object-schema@2.0.3': {} - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + string-width-cjs: string-width@4.2.3 strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 + strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true + wrap-ansi-cjs: wrap-ansi@7.0.0 - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/gen-mapping@0.3.5: - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - /@jridgewell/set-array@1.2.1: - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.2': {} - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/set-array@1.2.1': {} - /@jridgewell/trace-mapping@0.3.25: - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec@1.4.15': {} - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /@jsdevtools/ono@7.1.3: - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@jsdevtools/ono@7.1.3': {} - /@liuli-util/fs-extra@0.1.0: - resolution: {integrity: sha512-eaAyDyMGT23QuRGbITVY3SOJff3G9ekAAyGqB9joAnTBmqvFN+9a1FazOdO70G6IUqgpKV451eBHYSRcOJ/FNQ==} + '@liuli-util/fs-extra@0.1.0': dependencies: '@types/fs-extra': 9.0.13 fs-extra: 10.1.0 - /@mongodb-js/saslprep@1.1.7: - resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + '@mongodb-js/saslprep@1.1.7': dependencies: sparse-bitfield: 3.0.3 - dev: false - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@nodelib/fs.scandir@2.1.5': 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.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - dev: true - /@pagopa/eslint-config@3.0.0(typescript@5.4.5): - resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} + '@pagopa/eslint-config@3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5)': dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-config-prettier: 8.10.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.4.5) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0) + eslint-plugin-functional: 4.4.1(eslint@8.57.0)(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(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.10.0)(eslint@8.57.0)(prettier@2.8.8) + eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8) eslint-plugin-react: 7.34.3(eslint@8.57.0) eslint-plugin-sonarjs: 0.13.0(eslint@8.57.0) prettier: 2.8.8 @@ -5191,59 +9163,38 @@ packages: - supports-color - tsutils - typescript - dev: true - /@pagopa/interop-outbound-models@1.0.13: - resolution: {integrity: sha512-sn9urbiESbZuhMCcTdLOLWL1cp4CHoYf+R27Llk47RL0koed6dE4OEBvj71ILr0dA0A7NVjmeGEHWL3X3L8s7Q==} + '@pagopa/interop-outbound-models@1.0.11-d': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 zod: 3.23.8 - dev: false - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@protobuf-ts/plugin-framework@2.9.4: - resolution: {integrity: sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==} + '@protobuf-ts/plugin-framework@2.9.4': dependencies: '@protobuf-ts/runtime': 2.9.4 typescript: 3.9.10 - dev: false - /@protobuf-ts/plugin@2.9.4: - resolution: {integrity: sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==} - hasBin: true + '@protobuf-ts/plugin@2.9.4': dependencies: '@protobuf-ts/plugin-framework': 2.9.4 '@protobuf-ts/protoc': 2.9.4 '@protobuf-ts/runtime': 2.9.4 '@protobuf-ts/runtime-rpc': 2.9.4 typescript: 3.9.10 - dev: false - /@protobuf-ts/protoc@2.9.4: - resolution: {integrity: sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==} - hasBin: true - dev: false + '@protobuf-ts/protoc@2.9.4': {} - /@protobuf-ts/runtime-rpc@2.9.4: - resolution: {integrity: sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==} + '@protobuf-ts/runtime-rpc@2.9.4': dependencies: '@protobuf-ts/runtime': 2.9.4 - dev: false - /@protobuf-ts/runtime@2.9.4: - resolution: {integrity: sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==} + '@protobuf-ts/runtime@2.9.4': {} - /@puppeteer/browsers@2.2.3: - resolution: {integrity: sha512-bJ0UBsk0ESOs6RFcLXOt99a3yTDcOKlzfjad+rhFwdaG1Lu/Wzq58GHYCDTlZ9z6mldf4g+NTb+TXEfe0PpnsQ==} - engines: {node: '>=18'} - hasBin: true + '@puppeteer/browsers@2.2.3': dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -5256,240 +9207,117 @@ packages: transitivePeerDependencies: - supports-color - /@redis/bloom@1.2.0(@redis/client@1.5.17): - resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/bloom@1.2.0(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - - /@redis/client@1.5.17: - resolution: {integrity: sha512-IPvU9A31qRCZ7lds/x+ksuK/UMndd0EASveAvCvEtFFKIZjZ+m/a4a0L7S28KEWoR5ka8526hlSghDo4Hrc2Hg==} - engines: {node: '>=14'} + + '@redis/client@1.5.17': dependencies: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 - dev: false - /@redis/graph@1.1.1(@redis/client@1.5.17): - resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/graph@1.1.1(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/json@1.0.6(@redis/client@1.5.17): - resolution: {integrity: sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/json@1.0.6(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/search@1.1.6(@redis/client@1.5.17): - resolution: {integrity: sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/search@1.1.6(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@redis/time-series@1.0.5(@redis/client@1.5.17): - resolution: {integrity: sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==} - peerDependencies: - '@redis/client': ^1.0.0 + '@redis/time-series@1.0.5(@redis/client@1.5.17)': dependencies: '@redis/client': 1.5.17 - dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-x64@4.18.1': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true + '@sinclair/typebox@0.27.8': {} - /@sinonjs/commons@3.0.1: - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - dev: true - /@sinonjs/fake-timers@10.3.0: - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@sinonjs/fake-timers@10.3.0': dependencies: '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/fake-timers@11.3.1: - resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} + '@sinonjs/fake-timers@11.3.1': dependencies: '@sinonjs/commons': 3.0.1 - dev: true - /@sinonjs/samsam@8.0.2: - resolution: {integrity: sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==} + '@sinonjs/samsam@8.0.2': dependencies: '@sinonjs/commons': 3.0.1 lodash.get: 4.4.2 type-detect: 4.1.0 - dev: true - /@sinonjs/text-encoding@0.7.3: - resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} - dev: true + '@sinonjs/text-encoding@0.7.3': {} - /@smithy/abort-controller@3.1.8: - resolution: {integrity: sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==} - engines: {node: '>=16.0.0'} + '@smithy/abort-controller@3.1.8': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/chunked-blob-reader-native@3.0.1: - resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + '@smithy/chunked-blob-reader-native@3.0.1': dependencies: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/chunked-blob-reader@4.0.0: - resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + '@smithy/chunked-blob-reader@4.0.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/config-resolver@3.0.12: - resolution: {integrity: sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.12': dependencies: '@smithy/node-config-provider': 3.1.11 '@smithy/types': 3.7.1 @@ -5497,9 +9325,7 @@ packages: '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 - /@smithy/core@2.5.4: - resolution: {integrity: sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==} - engines: {node: '>=16.0.0'} + '@smithy/core@2.5.4': dependencies: '@smithy/middleware-serde': 3.0.10 '@smithy/protocol-http': 4.1.7 @@ -5510,20 +9336,15 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/credential-provider-imds@3.1.3: - resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.1.3': dependencies: '@smithy/node-config-provider': 3.1.11 '@smithy/property-provider': 3.1.10 '@smithy/types': 3.7.1 '@smithy/url-parser': 3.0.10 tslib: 2.6.3 - dev: false - /@smithy/credential-provider-imds@3.2.7: - resolution: {integrity: sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@3.2.7': dependencies: '@smithy/node-config-provider': 3.1.11 '@smithy/property-provider': 3.1.10 @@ -5531,62 +9352,45 @@ packages: '@smithy/url-parser': 3.0.10 tslib: 2.6.3 - /@smithy/eventstream-codec@3.1.9: - resolution: {integrity: sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==} + '@smithy/eventstream-codec@3.1.9': dependencies: '@aws-crypto/crc32': 5.2.0 '@smithy/types': 3.7.1 '@smithy/util-hex-encoding': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-browser@3.0.13: - resolution: {integrity: sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-browser@3.0.13': dependencies: '@smithy/eventstream-serde-universal': 3.0.12 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-config-resolver@3.0.10: - resolution: {integrity: sha512-K1M0x7P7qbBUKB0UWIL5KOcyi6zqV5mPJoL0/o01HPJr0CSq3A9FYuJC6e11EX6hR8QTIR++DBiGrYveOu6trw==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-config-resolver@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-node@3.0.12: - resolution: {integrity: sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-node@3.0.12': dependencies: '@smithy/eventstream-serde-universal': 3.0.12 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/eventstream-serde-universal@3.0.12: - resolution: {integrity: sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-universal@3.0.12': dependencies: '@smithy/eventstream-codec': 3.1.9 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/fetch-http-handler@3.2.9: - resolution: {integrity: sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==} + '@smithy/fetch-http-handler@3.2.9': dependencies: '@smithy/protocol-http': 4.1.7 '@smithy/querystring-builder': 3.0.10 '@smithy/types': 3.7.1 '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/fetch-http-handler@4.1.1: - resolution: {integrity: sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==} + '@smithy/fetch-http-handler@4.1.1': dependencies: '@smithy/protocol-http': 4.1.7 '@smithy/querystring-builder': 3.0.10 @@ -5594,87 +9398,64 @@ packages: '@smithy/util-base64': 3.0.0 tslib: 2.6.3 - /@smithy/hash-blob-browser@3.1.9: - resolution: {integrity: sha512-wOu78omaUuW5DE+PVWXiRKWRZLecARyP3xcq5SmkXUw9+utgN8HnSnBfrjL2B/4ZxgqPjaAJQkC/+JHf1ITVaQ==} + '@smithy/hash-blob-browser@3.1.9': dependencies: '@smithy/chunked-blob-reader': 4.0.0 '@smithy/chunked-blob-reader-native': 3.0.1 '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/hash-node@3.0.10: - resolution: {integrity: sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==} - engines: {node: '>=16.0.0'} + '@smithy/hash-node@3.0.10': dependencies: '@smithy/types': 3.7.1 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/hash-node@3.0.3: - resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} - engines: {node: '>=16.0.0'} + '@smithy/hash-node@3.0.3': dependencies: '@smithy/types': 3.7.1 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/hash-stream-node@3.1.9: - resolution: {integrity: sha512-3XfHBjSP3oDWxLmlxnt+F+FqXpL3WlXs+XXaB6bV9Wo8BBu87fK1dSEsyH7Z4ZHRmwZ4g9lFMdf08m9hoX1iRA==} - engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@3.1.9': dependencies: '@smithy/types': 3.7.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/invalid-dependency@3.0.10: - resolution: {integrity: sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==} + '@smithy/invalid-dependency@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/invalid-dependency@3.0.3: - resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.3': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/is-array-buffer@2.2.0: - resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} - engines: {node: '>=14.0.0'} + '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.6.3 - /@smithy/is-array-buffer@3.0.0: - resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} - engines: {node: '>=16.0.0'} + '@smithy/is-array-buffer@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/md5-js@3.0.10: - resolution: {integrity: sha512-m3bv6dApflt3fS2Y1PyWPUtRP7iuBlvikEOGwu0HsCZ0vE7zcIX+dBoh3e+31/rddagw8nj92j0kJg2TfV+SJA==} + '@smithy/md5-js@3.0.10': dependencies: '@smithy/types': 3.7.1 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/middleware-content-length@3.0.12: - resolution: {integrity: sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.12': dependencies: '@smithy/protocol-http': 4.1.7 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/middleware-endpoint@3.2.4: - resolution: {integrity: sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.2.4': dependencies: '@smithy/core': 2.5.4 '@smithy/middleware-serde': 3.0.10 @@ -5685,9 +9466,7 @@ packages: '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 - /@smithy/middleware-retry@3.0.28: - resolution: {integrity: sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.28': dependencies: '@smithy/node-config-provider': 3.1.11 '@smithy/protocol-http': 4.1.7 @@ -5699,32 +9478,24 @@ packages: tslib: 2.6.3 uuid: 9.0.1 - /@smithy/middleware-serde@3.0.10: - resolution: {integrity: sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/middleware-stack@3.0.10: - resolution: {integrity: sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/node-config-provider@3.1.11: - resolution: {integrity: sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.11': dependencies: '@smithy/property-provider': 3.1.10 '@smithy/shared-ini-file-loader': 3.1.11 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/node-http-handler@3.3.1: - resolution: {integrity: sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.3.1': dependencies: '@smithy/abort-controller': 3.1.8 '@smithy/protocol-http': 4.1.7 @@ -5732,68 +9503,48 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/property-provider@3.1.10: - resolution: {integrity: sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==} - engines: {node: '>=16.0.0'} + '@smithy/property-provider@3.1.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/property-provider@3.1.3: - resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} - engines: {node: '>=16.0.0'} + '@smithy/property-provider@3.1.3': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - dev: false - /@smithy/protocol-http@4.1.7: - resolution: {integrity: sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@4.1.7': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/querystring-builder@3.0.10: - resolution: {integrity: sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.10': dependencies: '@smithy/types': 3.7.1 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 - /@smithy/querystring-builder@3.0.3: - resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.3': dependencies: '@smithy/types': 3.7.1 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/querystring-parser@3.0.10: - resolution: {integrity: sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/service-error-classification@3.0.10: - resolution: {integrity: sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==} - engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.10': dependencies: '@smithy/types': 3.7.1 - /@smithy/shared-ini-file-loader@3.1.11: - resolution: {integrity: sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.11': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/signature-v4@2.3.0: - resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} - engines: {node: '>=14.0.0'} + '@smithy/signature-v4@2.3.0': dependencies: '@smithy/is-array-buffer': 2.2.0 '@smithy/types': 2.12.0 @@ -5802,11 +9553,8 @@ packages: '@smithy/util-uri-escape': 2.2.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - dev: false - /@smithy/signature-v4@3.1.2: - resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@3.1.2': dependencies: '@smithy/is-array-buffer': 3.0.0 '@smithy/types': 3.7.1 @@ -5815,11 +9563,8 @@ packages: '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - dev: false - /@smithy/signature-v4@4.2.3: - resolution: {integrity: sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@4.2.3': dependencies: '@smithy/is-array-buffer': 3.0.0 '@smithy/protocol-http': 4.1.7 @@ -5830,9 +9575,7 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/smithy-client@3.4.5: - resolution: {integrity: sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.4.5': dependencies: '@smithy/core': 2.5.4 '@smithy/middleware-endpoint': 3.2.4 @@ -5842,68 +9585,49 @@ packages: '@smithy/util-stream': 3.3.1 tslib: 2.6.3 - /@smithy/types@2.12.0: - resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} - engines: {node: '>=14.0.0'} + '@smithy/types@2.12.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/types@3.7.1: - resolution: {integrity: sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==} - engines: {node: '>=16.0.0'} + '@smithy/types@3.7.1': dependencies: tslib: 2.6.3 - /@smithy/url-parser@3.0.10: - resolution: {integrity: sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==} + '@smithy/url-parser@3.0.10': dependencies: '@smithy/querystring-parser': 3.0.10 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/util-base64@3.0.0: - resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-base64@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-body-length-browser@3.0.0: - resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + '@smithy/util-body-length-browser@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-body-length-node@3.0.0: - resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} - engines: {node: '>=16.0.0'} + '@smithy/util-body-length-node@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-buffer-from@2.2.0: - resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} - engines: {node: '>=14.0.0'} + '@smithy/util-buffer-from@2.2.0': dependencies: '@smithy/is-array-buffer': 2.2.0 tslib: 2.6.3 - /@smithy/util-buffer-from@3.0.0: - resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} - engines: {node: '>=16.0.0'} + '@smithy/util-buffer-from@3.0.0': dependencies: '@smithy/is-array-buffer': 3.0.0 tslib: 2.6.3 - /@smithy/util-config-provider@3.0.0: - resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-config-provider@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-defaults-mode-browser@3.0.28: - resolution: {integrity: sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.28': dependencies: '@smithy/property-provider': 3.1.10 '@smithy/smithy-client': 3.4.5 @@ -5911,9 +9635,7 @@ packages: bowser: 2.11.0 tslib: 2.6.3 - /@smithy/util-defaults-mode-node@3.0.28: - resolution: {integrity: sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.28': dependencies: '@smithy/config-resolver': 3.0.12 '@smithy/credential-provider-imds': 3.2.7 @@ -5923,53 +9645,37 @@ packages: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/util-endpoints@2.1.6: - resolution: {integrity: sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.1.6': dependencies: '@smithy/node-config-provider': 3.1.11 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/util-hex-encoding@2.2.0: - resolution: {integrity: sha512-7iKXR+/4TpLK194pVjKiasIyqMtTYJsgKgM242Y9uzt5dhHnUDvMNb+3xIhRJ9QhvqGii/5cRUt4fJn3dtXNHQ==} - engines: {node: '>=14.0.0'} + '@smithy/util-hex-encoding@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-hex-encoding@3.0.0: - resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-middleware@2.2.0: - resolution: {integrity: sha512-L1qpleXf9QD6LwLCJ5jddGkgWyuSvWBkJwWAZ6kFkdifdso+sk3L3O1HdmPvCdnCK3IS4qWyPxev01QMnfHSBw==} - engines: {node: '>=14.0.0'} + '@smithy/util-middleware@2.2.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.6.3 - dev: false - /@smithy/util-middleware@3.0.10: - resolution: {integrity: sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==} - engines: {node: '>=16.0.0'} + '@smithy/util-middleware@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/util-retry@3.0.10: - resolution: {integrity: sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==} - engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.10': dependencies: '@smithy/service-error-classification': 3.0.10 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@smithy/util-stream@3.3.1: - resolution: {integrity: sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.3.1': dependencies: '@smithy/fetch-http-handler': 4.1.1 '@smithy/node-http-handler': 3.3.1 @@ -5980,304 +9686,195 @@ packages: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - /@smithy/util-uri-escape@2.2.0: - resolution: {integrity: sha512-jtmJMyt1xMD/d8OtbVJ2gFZOSKc+ueYJZPW20ULW1GOp/q/YIM0wNh+u8ZFao9UaIGz4WoPW8hC64qlWLIfoDA==} - engines: {node: '>=14.0.0'} + '@smithy/util-uri-escape@2.2.0': dependencies: tslib: 2.6.3 - dev: false - /@smithy/util-uri-escape@3.0.0: - resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} - engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@3.0.0': dependencies: tslib: 2.6.3 - /@smithy/util-utf8@2.3.0: - resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} - engines: {node: '>=14.0.0'} + '@smithy/util-utf8@2.3.0': dependencies: '@smithy/util-buffer-from': 2.2.0 tslib: 2.6.3 - /@smithy/util-utf8@3.0.0: - resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} - engines: {node: '>=16.0.0'} + '@smithy/util-utf8@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 tslib: 2.6.3 - /@smithy/util-waiter@3.1.9: - resolution: {integrity: sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==} - engines: {node: '>=16.0.0'} + '@smithy/util-waiter@3.1.9': dependencies: '@smithy/abort-controller': 3.1.8 '@smithy/types': 3.7.1 tslib: 2.6.3 - /@testcontainers/postgresql@10.9.0: - resolution: {integrity: sha512-Z3K/TFkl/PVE2v8A6yKqgF4pSFk9ilFG02yeGhPswUjmBlcig/rpVOjBQOkQ/yJCcQ/r2RrX3RR+7vr+UO4QlQ==} + '@testcontainers/postgresql@10.9.0': dependencies: testcontainers: 10.9.0 transitivePeerDependencies: - encoding - supports-color - dev: true - - /@tootallnate/quickjs-emscripten@0.23.0: - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - - /@tsconfig/node-lts@20.1.3: - resolution: {integrity: sha512-m3b7EP2U+h5tNSpaBMfcTuHmHn04wrgRPQQrfKt75YIPq6kPs2153/KfPHdqkEWGx5pEBvS6rnvToT+yTtC1iw==} - dev: true - - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true + '@tootallnate/quickjs-emscripten@0.23.0': {} - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true + '@tsconfig/node-lts@20.1.3': {} - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true + '@tsconfig/strictest@2.0.5': {} - /@types/adm-zip@0.5.5: - resolution: {integrity: sha512-YCGstVMjc4LTY5uK9/obvxBya93axZOVOyf2GSUulADzmLhYE45u2nAssCs/fWBs1Ifq5Vat75JTPwd5XZoPJw==} + '@types/adm-zip@0.5.5': dependencies: '@types/node': 20.14.6 - dev: true - /@types/body-parser@1.19.5: - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 '@types/node': 20.14.6 - /@types/buffers@0.1.31: - resolution: {integrity: sha512-wEZBb3o0Kh5RAj3V172vJCcxaCV8C2HJ7YLBBlG5Mwue0g4uRg5LWv8C6ap8MyFbXE6UbYEuvtHY7oTWAPeXEw==} + '@types/buffers@0.1.31': dependencies: '@types/node': 20.14.6 - dev: false - /@types/connect@3.4.38: - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/connect@3.4.38': dependencies: '@types/node': 20.14.6 - /@types/docker-modem@3.0.6: - resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + '@types/docker-modem@3.0.6': dependencies: '@types/node': 20.14.6 '@types/ssh2': 1.15.0 - dev: true - /@types/dockerode@3.3.29: - resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==} + '@types/dockerode@3.3.29': dependencies: '@types/docker-modem': 3.0.6 '@types/node': 20.14.6 '@types/ssh2': 1.15.0 - dev: true - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/express-serve-static-core@4.19.5: - resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + '@types/express-serve-static-core@4.19.5': dependencies: '@types/node': 20.14.6 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - /@types/express@4.17.21: - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + '@types/express@4.17.21': dependencies: '@types/body-parser': 1.19.5 '@types/express-serve-static-core': 4.19.5 '@types/qs': 6.9.15 '@types/serve-static': 1.15.7 - /@types/fs-extra@9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + '@types/fs-extra@9.0.13': dependencies: '@types/node': 20.14.6 - /@types/html2json@1.0.1: - resolution: {integrity: sha512-D+cq6HcgfMdrbIpXzxsmJ9mBz9VlyagqaIB9OZVHGrOeyWbwjTLWR2qUGh1w/VZLtG4wy8mp7v4EpJQ1dYqWzw==} - dev: true + '@types/html2json@1.0.1': {} - /@types/http-errors@2.0.4: - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + '@types/http-errors@2.0.4': {} - /@types/json-diff@1.0.3: - resolution: {integrity: sha512-Qvxm8fpRMv/1zZR3sQWImeRK2mBYJji20xF51Fq9Gt//Ed18u0x6/FNLogLS1xhfUWTEmDyqveJqn95ltB6Kvw==} - dev: true + '@types/json-diff@1.0.3': {} - /@types/json-schema@7.0.15: - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true + '@types/json-schema@7.0.15': {} - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true + '@types/json5@0.0.29': {} - /@types/jsonwebtoken@9.0.6: - resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==} + '@types/jsonwebtoken@9.0.6': dependencies: '@types/node': 20.14.6 - /@types/lodash.isempty@4.4.9: - resolution: {integrity: sha512-DPSFfnT2JmZiAWNWOU8IRZws/Ha6zyGF5m06TydfsY+0dVoQqby2J61Na2QU4YtwiZ+moC6cJS6zWYBJq4wBVw==} + '@types/lodash.isempty@4.4.9': dependencies: '@types/lodash': 4.17.6 - dev: true - /@types/lodash.isequal@4.5.8: - resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + '@types/lodash.isequal@4.5.8': dependencies: '@types/lodash': 4.17.6 - dev: true - /@types/lodash@4.17.6: - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} - dev: true + '@types/lodash@4.17.6': {} - /@types/mime@1.3.5: - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/mime@1.3.5': {} - /@types/multer@1.4.11: - resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==} + '@types/multer@1.4.11': dependencies: '@types/express': 4.17.21 - dev: true - /@types/node@18.19.39: - resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==} + '@types/node@18.19.39': dependencies: undici-types: 5.26.5 - dev: true - /@types/node@20.14.6: - resolution: {integrity: sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==} + '@types/node@20.14.6': dependencies: undici-types: 5.26.5 - /@types/node@20.4.9: - resolution: {integrity: sha512-8e2HYcg7ohnTUbHk8focoklEQYvemQmu9M/f43DZVx43kHn0tE3BY/6gSDxS7k0SprtS0NHvj+L80cGLnoOUcQ==} - dev: true + '@types/node@20.4.9': {} - /@types/nodemailer@6.4.15: - resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==} + '@types/nodemailer@6.4.15': dependencies: '@types/node': 20.14.6 - dev: true - /@types/nodemailer@6.4.9: - resolution: {integrity: sha512-XYG8Gv+sHjaOtUpiuytahMy2mM3rectgroNbs6R3djZEKmPNiIJwe9KqOJBGzKKnNZNKvnuvmugBgpq3w/S0ig==} + '@types/nodemailer@6.4.9': dependencies: '@types/node': 20.14.6 - dev: true - /@types/qs@6.9.15: - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + '@types/qs@6.9.15': {} - /@types/range-parser@1.2.7: - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/range-parser@1.2.7': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/send@0.17.4: - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 '@types/node': 20.14.6 - /@types/serve-static@1.15.7: - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 '@types/node': 20.14.6 '@types/send': 0.17.4 - /@types/sinon@10.0.20: - resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} + '@types/sinon@10.0.20': dependencies: '@types/sinonjs__fake-timers': 8.1.5 - dev: true - /@types/sinonjs__fake-timers@8.1.5: - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - dev: true + '@types/sinonjs__fake-timers@8.1.5': {} - /@types/ssh2-sftp-client@9.0.4: - resolution: {integrity: sha512-gnIn56MTB9W3A3hPL/1sHI23t8YwcE3eVYa1O2XjT9vaqimFdtNHxyQiy5Y78+ociQTKazMSD8YyMEO4QjNMrg==} + '@types/ssh2-sftp-client@9.0.4': dependencies: '@types/ssh2': 1.15.0 - dev: true - /@types/ssh2-streams@0.1.12: - resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==} + '@types/ssh2-streams@0.1.12': dependencies: '@types/node': 20.14.6 - dev: true - /@types/ssh2@0.5.52: - resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + '@types/ssh2@0.5.52': dependencies: '@types/node': 20.14.6 '@types/ssh2-streams': 0.1.12 - dev: true - /@types/ssh2@1.15.0: - resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==} + '@types/ssh2@1.15.0': dependencies: '@types/node': 18.19.39 - dev: true - /@types/triple-beam@1.3.5: - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - dev: false + '@types/triple-beam@1.3.5': {} - /@types/uuid@9.0.8: - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/uuid@9.0.8': {} - /@types/webidl-conversions@7.0.3: - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false + '@types/webidl-conversions@7.0.3': {} - /@types/whatwg-url@11.0.5: - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 - dev: false - /@types/yauzl@2.10.3: - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - requiresBuild: true + '@types/yauzl@2.10.3': dependencies: '@types/node': 20.14.6 optional: true - /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - 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 + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.11.0 '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) @@ -6291,72 +9888,43 @@ packages: natural-compare-lite: 1.4.0 semver: 7.6.2 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - 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 + '@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) debug: 4.3.5 eslint: 8.57.0 + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - - /@typescript-eslint/scope-manager@5.62.0: - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - dev: true - /@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/type-utils@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) debug: 4.3.5 eslint: 8.57.0 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/types@5.62.0: - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true + '@typescript-eslint/types@5.62.0': {} - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5): - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 @@ -6365,16 +9933,12 @@ packages: is-glob: 4.0.3 semver: 7.6.2 tsutils: 3.21.0(typescript@5.4.5) + optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 @@ -6388,199 +9952,126 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/visitor-keys@5.62.0: - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@typescript-eslint/visitor-keys@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - dev: true - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - dev: true + '@ungap/structured-clone@1.2.0': {} - /@vitest/expect@1.6.0: - resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} + '@vitest/expect@1.6.0': dependencies: '@vitest/spy': 1.6.0 '@vitest/utils': 1.6.0 chai: 4.4.1 - dev: true - /@vitest/runner@1.6.0: - resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} + '@vitest/runner@1.6.0': dependencies: '@vitest/utils': 1.6.0 p-limit: 5.0.0 pathe: 1.1.2 - dev: true - /@vitest/snapshot@1.6.0: - resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} + '@vitest/snapshot@1.6.0': dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 - dev: true - /@vitest/spy@1.6.0: - resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} + '@vitest/spy@1.6.0': dependencies: tinyspy: 2.2.1 - dev: true - /@vitest/utils@1.6.0: - resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} + '@vitest/utils@1.6.0': dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 loupe: 2.3.7 pretty-format: 29.7.0 - dev: true - /@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8): - resolution: {integrity: sha512-aH4rOdb3AcezN7ws8vDgBfGboZMk2JGGzEq/DtW65MhnRxyTGRuLJRWVQ/2KxDgWvV2F5oTkAS+5pnjKbl0n+A==} - peerDependencies: - axios: ^0.x || ^1.0.0 - zod: ^3.x + '@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8)': dependencies: axios: 1.7.4 zod: 3.23.8 - /@zodios/express@10.6.1(@zodios/core@10.9.6)(express@4.20.0)(zod@3.23.8): - resolution: {integrity: sha512-FNgOq8mvwvWP5B2howMKGm6EPp6i/0XFAsQnX5Ov3MLbanzD1oE4WJtBkTL3cmJYvD0nyykbWSeHOh51bCmhUA==} - peerDependencies: - '@zodios/core': '>=10.4.4 <11.0.0' - express: 4.x - zod: ^3.x + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8)': + dependencies: + '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) + express: 4.19.2 + zod: 3.23.8 + + '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8)': dependencies: '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) express: 4.20.0 zod: 3.23.8 - dev: false - /accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: false - /acorn-jsx@2.0.1: - resolution: {integrity: sha512-rbNtu2WkMJAZNnw2rh35whZO2e2N8Q1Dp4PBf/pKJAals6uFbPvVgVcKZ8poUnrkF50thOea1ApmF8W56apnwA==} + acorn-jsx@2.0.1: dependencies: acorn: 2.7.0 - dev: true - /acorn-jsx@5.3.2(acorn@8.12.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: true - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 - dev: true - /acorn@2.7.0: - resolution: {integrity: sha512-pXK8ez/pVjqFdAgBkF1YPVRacuLQ9EXBKaKWaeh58WNfMkCmZhOZzu+NtKSPD5PHmCCHheQ5cD29qM1K4QTxIg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true + acorn@2.7.0: {} - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true + acorn@8.12.1: {} - /adm-zip@0.5.15: - resolution: {integrity: sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==} - engines: {node: '>=12.0'} - dev: false + adm-zip@0.5.15: {} - /agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} - engines: {node: '>= 14'} + agent-base@7.1.1: dependencies: debug: 4.3.5 transitivePeerDependencies: - supports-color - /ajv-draft-04@1.0.0(ajv@8.16.0): - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: + ajv-draft-04@1.0.0(ajv@8.16.0): + optionalDependencies: ajv: 8.16.0 - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.12.6: 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 - /ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true + ansi-styles@5.2.0: {} - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /append-field@1.0.0: - resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - dev: false + append-field@1.0.0: {} - /archiver-utils@2.1.0: - resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} - engines: {node: '>= 6'} + archiver-utils@2.1.0: dependencies: glob: 7.2.3 graceful-fs: 4.2.11 @@ -6592,11 +10083,8 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 2.3.8 - dev: true - /archiver-utils@3.0.4: - resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} - engines: {node: '>= 10'} + archiver-utils@3.0.4: dependencies: glob: 7.2.3 graceful-fs: 4.2.11 @@ -6608,11 +10096,8 @@ packages: lodash.union: 4.6.0 normalize-path: 3.0.0 readable-stream: 3.6.2 - dev: true - /archiver@5.3.2: - resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} - engines: {node: '>= 10'} + archiver@5.3.2: dependencies: archiver-utils: 2.1.0 async: 3.2.5 @@ -6621,35 +10106,21 @@ packages: readdir-glob: 1.1.3 tar-stream: 2.2.0 zip-stream: 4.1.1 - dev: true - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 - dev: true - /array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - dev: false + array-flatten@1.1.1: {} - /array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -6657,16 +10128,10 @@ packages: es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 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-union@2.1.0: {} - /array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -6674,11 +10139,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -6686,51 +10148,37 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + array.prototype.toreversed@1.1.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: true - /array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - dev: true - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -6740,76 +10188,53 @@ packages: get-intrinsic: 1.2.4 is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 - dev: true - /asn1@0.2.6: - resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + asn1@0.2.6: dependencies: safer-buffer: 2.1.2 - /assert-options@0.8.1: - resolution: {integrity: sha512-5lNGRB5g5i2bGIzb+J1QQE1iKU/WEMVBReFIc5pPDWjcPj23otPL0eI6PB2v7QPi0qU6Mhym5D3y0ZiSIOf3GA==} - engines: {node: '>=10.0.0'} + assert-options@0.8.1: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} + ast-types@0.13.4: dependencies: tslib: 2.6.3 - /async-lock@1.4.1: - resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} - dev: true + async-lock@1.4.1: {} - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.5: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + asynckit@0.4.0: {} - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - dev: true - /aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.693.0): - resolution: {integrity: sha512-L0Jk0k2XNHMSGipJ8rRdTq51KrH/gwrfZ39iKY9BWHGOAv7EygsG4qJC7lIRsbu5/ZHB886Z3WsOsFxqR2R4XQ==} - engines: {node: '>=14.x'} + aws-msk-iam-sasl-signer-js@1.0.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)): dependencies: '@aws-crypto/sha256-js': 4.0.0 '@aws-sdk/client-sts': 3.693.0 - '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0) + '@aws-sdk/credential-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/util-format-url': 3.609.0 '@smithy/signature-v4': 2.3.0 '@types/buffers': 0.1.31 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - dev: false - /aws-sdk-client-mock@4.0.1: - resolution: {integrity: sha512-yD2mmgy73Xce097G5hIpr1k7j50qzvJ49/+6osGZiCyk4m6cwhb+2x7kKFY1gEMwTzaS8+m8fXv9SB29SkRYyQ==} + aws-sdk-client-mock@4.0.1: dependencies: '@types/sinon': 10.0.20 sinon: 16.1.3 tslib: 2.6.3 - dev: true - /axios-logger@2.8.1: - resolution: {integrity: sha512-Bbl7XRR/Rkxg2Owv/kRgAZ/0qf8kMPLc08LtiUcGCWV5RmoI7vHr+eee6SUc8jRi2nE5KWShziCVh35C1SBEaw==} + axios-logger@2.8.1: dependencies: chalk: 4.1.2 dateformat: 3.0.3 - dev: false - /axios@1.7.4: - resolution: {integrity: sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==} + axios@1.7.4: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -6817,69 +10242,65 @@ packages: transitivePeerDependencies: - debug - /b4a@1.6.6: - resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + b4a@1.6.6: {} - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /bare-events@2.4.2: - resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} - requiresBuild: true + bare-events@2.4.2: optional: true - /bare-fs@2.3.1: - resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} - requiresBuild: true + bare-fs@2.3.1: dependencies: bare-events: 2.4.2 bare-path: 2.1.3 bare-stream: 2.1.3 optional: true - /bare-os@2.4.0: - resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} - requiresBuild: true + bare-os@2.4.0: optional: true - /bare-path@2.1.3: - resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} - requiresBuild: true + bare-path@2.1.3: dependencies: bare-os: 2.4.0 optional: true - /bare-stream@2.1.3: - resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} - requiresBuild: true + bare-stream@2.1.3: dependencies: streamx: 2.18.0 optional: true - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64-js@1.5.1: {} - /basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} + basic-ftp@5.0.5: {} - /bcrypt-pbkdf@1.0.2: - resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@1.20.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + body-parser@1.20.3: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -6895,92 +10316,56 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /bowser@2.11.0: - resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + bowser@2.11.0: {} - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /browserslist@4.23.1: - resolution: {integrity: sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.1: dependencies: caniuse-lite: 1.0.30001640 electron-to-chromium: 1.4.818 node-releases: 2.0.14 update-browserslist-db: 1.1.0(browserslist@4.23.1) - /bson@6.8.0: - resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} - engines: {node: '>=16.20.1'} - dev: false + bson@6.8.0: {} - /buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-crc32@0.2.13: {} - /buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-equal-constant-time@1.0.1: {} - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: false + buffer-from@1.1.2: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /buildcheck@0.0.6: - resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} - engines: {node: '>=10.0.0'} - requiresBuild: true + buildcheck@0.0.6: optional: true - /busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} + busboy@1.6.0: dependencies: streamsearch: 1.1.0 - dev: false - /byline@5.0.0: - resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} - engines: {node: '>=0.10.0'} - dev: true + byline@5.0.0: {} - /bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - dev: false + bytes@3.1.2: {} - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + cac@6.7.14: {} - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 @@ -6988,19 +10373,13 @@ packages: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - /call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + call-me-maybe@1.0.2: {} - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /caniuse-lite@1.0.30001640: - resolution: {integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==} + caniuse-lite@1.0.30001640: {} - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} + chai@4.4.1: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -7009,212 +10388,133 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 - dev: true - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - dev: true + chownr@1.1.4: {} - /chromium-bidi@0.5.23(devtools-protocol@0.0.1299070): - resolution: {integrity: sha512-1o/gLU9wDqbN5nL2MtfjykjOuighGXc3/hnWueO1haiEoFgX8h5vbvcA4tgdQfjw1mkZ1OEF4x/+HVeqEX6NoA==} - peerDependencies: - devtools-protocol: '*' + chromium-bidi@0.5.23(devtools-protocol@0.0.1299070): dependencies: devtools-protocol: 0.0.1299070 mitt: 3.0.1 urlpattern-polyfill: 10.0.0 zod: 3.23.8 - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - /cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - dev: false + cluster-key-slot@1.1.2: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@3.2.1: dependencies: color-convert: 1.9.3 color-string: 1.9.1 - dev: false - /colors@1.4.0: - resolution: {integrity: sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==} - engines: {node: '>=0.1.90'} - dev: false + colors@1.4.0: {} - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + colorspace@1.1.4: dependencies: color: 3.2.1 text-hex: 1.0.0 - dev: false - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - /comment-parser@1.3.1: - resolution: {integrity: sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==} - engines: {node: '>= 12.0.0'} - dev: true + comment-parser@1.3.1: {} - /compress-commons@4.1.2: - resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} - engines: {node: '>= 10'} + compress-commons@4.1.2: dependencies: buffer-crc32: 0.2.13 crc32-stream: 4.0.3 normalize-path: 3.0.0 readable-stream: 3.6.2 - dev: true - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + concat-map@0.0.1: {} - /concat-stream@1.6.2: - resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} - engines: {'0': node >= 0.8} + concat-stream@1.6.2: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 readable-stream: 2.3.8 typedarray: 0.0.6 - dev: false - /concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} + concat-stream@2.0.0: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 readable-stream: 3.6.2 typedarray: 0.0.6 - dev: false - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true + confbox@0.1.7: {} - /connection-string@4.4.0: - resolution: {integrity: sha512-D4xsUjSoE8m/B5yMOvCIHY+2ME6FIZhCq0NzBBT57Q8BuL7ArFhBK04osOfReoW4KFr5ztzFwWRdmnv9rCvu2w==} - engines: {node: '>=14'} - dev: false + connection-string@4.4.0: {} - /console-assert@1.0.0: - resolution: {integrity: sha512-YtowQtZLdzPUlXL+kxMEBclXVOrWzR/+9TAUbIdgnjCkRW8+Dj0y4ajMJtOoQFXEubMONX0fkFS3SNLxx4FQRA==} - dev: true + console-assert@1.0.0: {} - /content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 - dev: false - /content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - dev: false + content-type@1.0.5: {} - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convert-source-map@2.0.0: {} - /cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - dev: false + cookie-signature@1.0.6: {} - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.6.0: {} - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + core-util-is@1.0.3: {} - /cosmiconfig@9.0.0(typescript@5.4.5): - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@9.0.0(typescript@5.4.5): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 + optionalDependencies: typescript: 5.4.5 - /cpu-features@0.0.10: - resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} - engines: {node: '>=10.0.0'} - requiresBuild: true + cpu-features@0.0.10: dependencies: buildcheck: 0.0.6 nan: 2.20.0 optional: true - /cpx2@7.0.1: - resolution: {integrity: sha512-ZgK/DRvPFM5ATZ5DQ5UzY6ajkBrI/p9Uc7VyLHc7b4OSFeBO4yOQz/GEmccc4Om6capGYlY4K1XX+BtYQiPPIA==} - engines: {node: '>=18', npm: '>=10'} - hasBin: true + cpx2@7.0.1: dependencies: debounce: 2.1.0 debug: 4.3.5 @@ -7231,253 +10531,132 @@ packages: subarg: 1.0.0 transitivePeerDependencies: - supports-color - dev: true - /crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - dev: true + crc-32@1.2.2: {} - /crc32-stream@4.0.3: - resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} - engines: {node: '>= 10'} + crc32-stream@4.0.3: dependencies: crc-32: 1.2.2 readable-stream: 3.6.2 - dev: true - /create-eslint-index@1.0.0: - resolution: {integrity: sha512-nXvJjnfDytOOaPOonX0h0a1ggMoqrhdekGeZkD6hkcWYvlCWhU719tKFVh8eU04CnMwu3uwe1JjwuUF2C3k2qg==} - engines: {node: '>=4.0.0'} + create-eslint-index@1.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'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true - /csv-generate@4.4.2: - resolution: {integrity: sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==} - dev: false + csv-generate@4.4.2: {} - /csv-parse@5.6.0: - resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} - dev: false + csv-parse@5.6.0: {} - /csv-stringify@6.5.2: - resolution: {integrity: sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==} - dev: false + csv-stringify@6.5.2: {} - /csv@6.3.2: - resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} - engines: {node: '>= 0.1.90'} + csv@6.3.2: dependencies: csv-generate: 4.4.2 csv-parse: 5.6.0 csv-stringify: 6.5.2 stream-transform: 3.3.3 - dev: false - /data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} + data-uri-to-buffer@6.0.2: {} - /data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - dev: true - /date-fns-tz@3.1.3(date-fns@3.6.0): - resolution: {integrity: sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==} - peerDependencies: - date-fns: ^3.0.0 + date-fns-tz@3.1.3(date-fns@3.6.0): dependencies: date-fns: 3.6.0 - dev: false - /date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + date-fns@3.6.0: {} - /dateformat@3.0.3: - resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} - dev: false + dateformat@3.0.3: {} - /debounce@2.1.0: - resolution: {integrity: sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==} - engines: {node: '>=18'} - dev: true + debounce@2.1.0: {} - /debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@2.6.9: dependencies: ms: 2.0.0 - dev: false - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: true - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.4: dependencies: ms: 2.1.2 - /debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5: dependencies: ms: 2.1.2 - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.0.8 - dev: true - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true + deep-is@0.1.4: {} - /deepmerge-ts@4.3.0: - resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==} - engines: {node: '>=12.4.0'} - dev: true + deepmerge-ts@4.3.0: {} - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 gopd: 1.0.1 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - dev: true - /degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 escodegen: 2.1.0 esprima: 4.0.1 - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - /depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dev: false + delayed-stream@1.0.0: {} - /depseek@0.4.1: - resolution: {integrity: sha512-YYfPPajzH9s2qnEva411VJzCMWtArBTfluI9USiKQ+T6xBWFh3C7yPxhaa1KVgJa17v9aRKc+LcRhgxS5/9mOA==} - dev: true + depd@2.0.0: {} - /destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - dev: false + depseek@0.4.1: {} - /devtools-protocol@0.0.1299070: - resolution: {integrity: sha512-+qtL3eX50qsJ7c+qVyagqi7AWMoQCBGNfoyJZMwm/NSXVqLYbuitrWEEIzxfUmTNy7//Xe8yhMmQ+elj3uAqSg==} + destroy@1.2.0: {} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + devtools-protocol@0.0.1299070: {} - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true + diff-sequences@29.6.3: {} - /diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - dev: true + diff@5.2.0: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /docker-compose@0.24.8: - resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==} - engines: {node: '>= 6.0.0'} + docker-compose@0.24.8: dependencies: yaml: 2.4.5 - dev: true - /docker-modem@3.0.8: - resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==} - engines: {node: '>= 8.0'} + docker-modem@3.0.8: dependencies: debug: 4.3.5 readable-stream: 3.6.2 @@ -7485,117 +10664,70 @@ packages: ssh2: 1.15.0 transitivePeerDependencies: - supports-color - dev: true - /dockerode@3.3.5: - resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==} - engines: {node: '>= 8.0'} + dockerode@3.3.5: dependencies: '@balena/dockerignore': 1.0.2 docker-modem: 3.0.8 tar-fs: 2.0.1 transitivePeerDependencies: - supports-color - dev: true - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: true - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + doctrine@3.0.0: dependencies: esutils: 2.0.3 - dev: true - /dotenv-flow@4.1.0: - resolution: {integrity: sha512-0cwP9jpQBQfyHwvE0cRhraZMkdV45TQedA8AAUZMsFzvmLcQyc1HPv+oX0OOYwLFjIlvgVepQ+WuQHbqDaHJZg==} - engines: {node: '>= 12.0.0'} + dotenv-flow@4.1.0: dependencies: dotenv: 16.4.5 - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} + dotenv@16.4.5: {} - /drange@1.1.1: - resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} - engines: {node: '>=4'} - dev: true + drange@1.1.1: {} - /dreamopt@0.8.0: - resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==} - engines: {node: '>=0.4.0'} + dreamopt@0.8.0: dependencies: wordwrap: 1.0.0 - dev: false - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: true + duplexer@0.1.2: {} - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 - /ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - dev: false + ee-first@1.1.1: {} - /electron-to-chromium@1.4.818: - resolution: {integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==} + electron-to-chromium@1.4.818: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true + emoji-regex@9.2.2: {} - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false + enabled@2.0.0: {} - /encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@1.0.2: {} - /encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - dev: false + encodeurl@2.0.0: {} - /end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + end-of-stream@1.4.4: dependencies: once: 1.4.0 - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} + env-paths@2.2.1: {} - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: false + err-code@2.0.3: {} - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - /es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 @@ -7643,21 +10775,14 @@ packages: typed-array-length: 1.0.6 unbox-primitive: 1.0.2 which-typed-array: 1.1.15 - dev: true - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + es-errors@1.3.0: {} - /es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} - engines: {node: '>= 0.4'} + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -7673,44 +10798,28 @@ packages: internal-slot: 1.0.7 iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 - dev: true - /es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 - dev: true - /es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 hasown: 2.0.2 - dev: true - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.0.2: dependencies: hasown: 2.0.2 - dev: true - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - dev: true - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 '@esbuild/android-arm': 0.21.5 @@ -7735,13 +10844,8 @@ packages: '@esbuild/win32-arm64': 0.21.5 '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - dev: true - /esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + esbuild@0.23.1: optionalDependencies: '@esbuild/aix-ppc64': 0.23.1 '@esbuild/android-arm': 0.23.1 @@ -7767,29 +10871,16 @@ packages: '@esbuild/win32-arm64': 0.23.1 '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 - dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} + escalade@3.1.2: {} - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false + escape-html@1.0.3: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: true + escape-string-regexp@4.0.0: {} - /escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true + escodegen@2.1.0: dependencies: esprima: 4.0.1 estraverse: 5.3.0 @@ -7797,118 +10888,62 @@ packages: optionalDependencies: source-map: 0.6.1 - /eslint-ast-utils@1.1.0: - resolution: {integrity: sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==} - engines: {node: '>=4'} + eslint-ast-utils@1.1.0: dependencies: lodash.get: 4.4.2 lodash.zip: 4.2.0 - dev: true - /eslint-config-prettier@8.10.0(eslint@8.57.0): - resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + eslint-config-prettier@8.10.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.14.0 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - 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 + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-extra-rules@0.0.0-development: - resolution: {integrity: sha512-Lib5tzYuLE8IneAYm8LY5oFhAaQ40IgO8BemKZGBpmZgQwgG7zzKLHs+pvUcgn5cjdoPdbZMcr2vTYmuss2l/g==} - engines: {node: '> 0.10.*'} + eslint-plugin-extra-rules@0.0.0-development: 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.57.0): - resolution: {integrity: sha512-3n2oHibwoIxAht9/+ZaTldhI6brXORgl8oNXqZd+d9xuAQt2SBJ2/aml0oQRMWvXrgsz2WG6wfC++NjzSG3prA==} - engines: {node: '>=4.0.0'} - peerDependencies: - eslint: '>=3' + eslint-plugin-fp@2.3.0(eslint@8.57.0): dependencies: create-eslint-index: 1.0.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.57.0)(typescript@5.4.5): - 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 + eslint-plugin-functional@4.4.1(eslint@8.57.0)(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) deepmerge-ts: 4.3.0 escape-string-regexp: 4.0.0 eslint: 8.57.0 semver: 7.6.2 + optionalDependencies: + tsutils: 3.21.0(typescript@5.4.5) typescript: 5.4.5 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0)(eslint@8.57.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -7917,7 +10952,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3 @@ -7927,17 +10962,14 @@ packages: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.57.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): - resolution: {integrity: sha512-Rq2QY6BZP2meNIs48aZ3GlIlJgBqFCmR55+UBvaDkA3ZNQ0SvQXOs2QKkubakEijV8UbIVbVZKsOVN8G3MuqZw==} - engines: {node: ^14 || ^16 || ^17 || ^18 || ^19} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint-plugin-jsdoc@39.9.1(eslint@8.57.0): dependencies: '@es-joy/jsdoccomment': 0.36.1 comment-parser: 1.3.1 @@ -7949,38 +10981,20 @@ packages: spdx-expression-parse: 3.0.1 transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): - resolution: {integrity: sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==} - peerDependencies: - eslint: '>=2.0.0' + eslint-plugin-prefer-arrow@1.2.3(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0)(eslint@8.57.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 + eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: eslint: 8.57.0 - eslint-config-prettier: 8.10.0(eslint@8.57.0) prettier: 2.8.8 prettier-linter-helpers: 1.0.0 - dev: true + optionalDependencies: + eslint-config-prettier: 8.10.0(eslint@8.57.0) - /eslint-plugin-react@7.34.3(eslint@8.57.0): - resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint-plugin-react@7.34.3(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -8001,42 +11015,24 @@ packages: resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.11 - dev: true - /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 + eslint-plugin-sonarjs@0.13.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: true - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.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} + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.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-visitor-keys@3.4.3: {} - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true + eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.11.0 @@ -8078,75 +11074,43 @@ packages: 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 + espree@3.0.0-alpha-1: dependencies: acorn: 2.7.0 acorn-jsx: 2.0.1 - 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} + espree@9.6.1: dependencies: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 - dev: true - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true + esprima@4.0.1: {} - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} + esquery@1.5.0: dependencies: estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.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'} + estraverse@4.3.0: {} - /estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + estraverse@5.3.0: {} + + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.5 - dev: true - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + esutils@2.0.3: {} - /etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - dev: false + etag@1.8.1: {} - /eval-estree-expression@2.0.0: - resolution: {integrity: sha512-e1VweC8biANiuLBY1kZSva3PpaiqaL79S9RR9ql9ngidCYM6tsY20xrUBEJsN63A1yVSmO92chPaBpIGBmGsPg==} + eval-estree-expression@2.0.0: {} - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 get-stream: 8.0.1 @@ -8157,11 +11121,44 @@ packages: onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 - dev: true - /express@4.20.0: - resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} - engines: {node: '>= 0.10.0'} + express@4.19.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.2 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.6.0 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.2.0 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.1 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.7 + proxy-addr: 2.0.7 + qs: 6.11.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.18.0 + serve-static: 1.15.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@4.20.0: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 @@ -8196,12 +11193,8 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color - dev: false - /extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true + extract-zip@2.0.1: dependencies: debug: 4.3.5 get-stream: 5.2.0 @@ -8211,80 +11204,51 @@ packages: transitivePeerDependencies: - supports-color - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: true + fast-diff@1.3.0: {} - /fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-fifo@1.3.2: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: 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.7 - dev: true - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true + fast-levenshtein@2.0.6: {} - /fast-xml-parser@4.2.5: - resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} - hasBin: true + fast-xml-parser@4.2.5: dependencies: strnum: 1.0.5 - dev: false - /fast-xml-parser@4.4.1: - resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} - hasBin: true + fast-xml-parser@4.4.1: dependencies: strnum: 1.0.5 - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - dev: true - /fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fd-slicer@1.1.0: dependencies: pend: 1.2.0 - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false + fecha@4.2.3: {} - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - dev: true - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} + finalhandler@1.2.0: dependencies: debug: 2.6.9 encodeurl: 1.0.2 @@ -8295,147 +11259,84 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /find-index@0.1.1: - resolution: {integrity: sha512-uJ5vWrfBKMcE6y2Z8834dwEZj9mNGxYa3t3I53OwFeuZ8D9oc2E5zcsrkuhX6h4iYrjhiv0T3szQmxlAV9uxDg==} - dev: true + find-index@0.1.1: {} - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@3.2.0: dependencies: flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 - dev: true - /flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - dev: true + flatted@3.3.1: {} - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false + fn.name@1.1.0: {} - /follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true + follow-redirects@1.15.6: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.3: dependencies: is-callable: 1.2.7 - dev: true - /foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - dev: false + forwarded@0.2.0: {} - /fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - dev: false + fresh@0.5.2: {} - /fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: true + fs-constants@1.0.0: {} - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - /fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 functions-have-names: 1.2.3 - dev: true - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: true + functions-have-names@1.2.3: {} - /generic-pool@3.9.0: - resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} - engines: {node: '>= 4'} - dev: false + generic-pool@3.9.0: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + get-caller-file@2.0.5: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-func-name@2.0.2: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 @@ -8443,40 +11344,25 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-port@5.1.1: - resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} - engines: {node: '>=8'} - dev: true + get-port@5.1.1: {} - /get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} + get-stream@5.2.0: dependencies: pump: 3.0.0 - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true + get-stream@8.0.1: {} - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - dev: true - /get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.8.1: dependencies: resolve-pkg-maps: 1.0.0 - dev: true - /get-uri@6.0.3: - resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} - engines: {node: '>= 14'} + get-uri@6.0.3: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 @@ -8485,31 +11371,19 @@ packages: transitivePeerDependencies: - supports-color - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - dev: true - /glob2base@0.0.12: - resolution: {integrity: sha512-ZyqlgowMbfj2NPjxaZZ/EtsXlOch28FRXgMd64vqZWk1bT9+wvSRLYD1om9M7QfQru51zJPAT17qXm4/zd+9QA==} - engines: {node: '>= 0.10'} + glob2base@0.0.12: dependencies: find-index: 0.1.1 - dev: true - /glob@10.4.3: - resolution: {integrity: sha512-Q38SGlYRpVtDBPSWEylRyctn7uDeTp4NQERTLiCT1FqA9JXPYWqAVmQU6qh4r/zMM5ehxTcbaO8EjhWnvEhmyg==} - engines: {node: '>=18'} - hasBin: true + glob@10.4.3: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.1 @@ -8517,11 +11391,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -8529,30 +11400,19 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@13.24.0: dependencies: type-fest: 0.20.2 - dev: true - /globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.0.1 - dev: true - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -8560,35 +11420,24 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /globby@13.2.2: - resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + globby@13.2.2: dependencies: dir-glob: 3.0.1 fast-glob: 3.3.2 ignore: 5.3.1 merge2: 1.4.1 slash: 4.0.0 - dev: true - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graceful-fs@4.2.11: {} - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - dev: true + graphemer@1.4.0: {} - /handlebars@4.7.7: - resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==} - engines: {node: '>=0.4.7'} - hasBin: true + handlebars@4.7.7: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -8596,12 +11445,8 @@ packages: wordwrap: 1.0.0 optionalDependencies: uglify-js: 3.18.0 - dev: true - /handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true + handlebars@4.7.8: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -8610,453 +11455,265 @@ packages: optionalDependencies: uglify-js: 3.18.0 - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true + has-bigints@1.0.2: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.0.3: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - dev: true - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /heap@0.2.7: - resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==} - dev: false + heap@0.2.7: {} - /html2json@1.0.2: - resolution: {integrity: sha512-tCdVt82U+/D1GCXFIoN5VfCzx767065EZJ5B8nStQUGSXU9PQ4L/0kFvF2of3Qsoe9HGaJni+lxOkqakfw0zpA==} - dev: false + html2json@1.0.2: {} - /http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} + http-errors@2.0.0: dependencies: depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 statuses: 2.0.1 toidentifier: 1.0.1 - dev: false - /http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 debug: 4.3.5 transitivePeerDependencies: - supports-color - /https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} - engines: {node: '>= 14'} + https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 debug: 4.3.5 transitivePeerDependencies: - supports-color - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true + human-signals@5.0.0: {} - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: false - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ieee754@1.2.1: {} - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 - dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - dev: false + ipaddr.js@1.9.1: {} - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.2.1: {} - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + is-arrayish@0.3.2: {} - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - dev: true - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true + is-callable@1.2.7: {} - /is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} + is-core-module@2.14.0: dependencies: hasown: 2.0.2 - dev: true - /is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 - dev: true - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + is-fullwidth-code-point@3.0.0: {} - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} + is-generator-function@1.0.10: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - /is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - dev: true + is-map@2.0.3: {} - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - dev: true + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-number@7.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-path-inside@3.0.3: {} - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - dev: true - /is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - dev: true + is-set@2.0.3: {} - /is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 - dev: true - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false + is-stream@2.0.1: {} - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-stream@3.0.0: {} - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 - dev: true - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 - dev: true - /is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - dev: true + is-weakmap@2.0.2: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - dev: true - /is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} - engines: {node: '>= 0.4'} + is-weakset@2.0.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: true - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@1.0.0: {} - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true + isarray@2.0.5: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 get-intrinsic: 1.2.4 has-symbols: 1.0.3 reflect.getprototypeof: 1.0.6 set-function-name: 2.0.2 - dev: true - /jackspeak@3.4.1: - resolution: {integrity: sha512-U23pQPDnmYybVkYjObcuYMk43VRlMLLqLI+RdZy8s8WV8WsxO9SnqSroKaluuvcNOdCAlauKszDwd+umbot5Mg==} - engines: {node: '>=18'} + jackspeak@3.4.1: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - dev: false + jose@4.15.9: {} - /jose@5.9.4: - resolution: {integrity: sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==} - dev: false + jose@5.9.4: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@4.0.0: {} - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: true + js-tokens@9.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsbn@1.1.0: {} - /jsdoc-type-pratt-parser@3.1.0: - resolution: {integrity: sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==} - engines: {node: '>=12.0.0'} - dev: true + jsdoc-type-pratt-parser@3.1.0: {} - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + json-buffer@3.0.1: {} - /json-diff@1.0.6: - resolution: {integrity: sha512-tcFIPRdlc35YkYdGxcamJjllUhXWv4n2rK9oJ2RsAzV4FBkuV4ojKEDgcZ+kpKxDmJKv+PFK65+1tVVOnSeEqA==} - hasBin: true + json-diff@1.0.6: dependencies: '@ewoudenberg/difflib': 0.1.0 colors: 1.4.0 dreamopt: 0.8.0 - dev: false - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@2.3.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true + json-schema-traverse@0.4.1: {} - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true + json-stable-stringify-without-jsonify@1.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: true - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true + json5@2.2.3: {} - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - /jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} - engines: {node: '>=12', npm: '>=6'} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 lodash.includes: 4.3.0 @@ -9069,30 +11726,22 @@ packages: ms: 2.1.3 semver: 7.6.2 - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 object.values: 1.2.0 - dev: true - /just-extend@6.2.0: - resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} - dev: true + just-extend@6.2.0: {} - /jwa@1.4.1: - resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + jwa@1.4.1: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - /jwks-rsa@3.1.0: - resolution: {integrity: sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==} - engines: {node: '>=14'} + jwks-rsa@3.1.0: dependencies: '@types/express': 4.17.21 '@types/jsonwebtoken': 9.0.6 @@ -9102,133 +11751,79 @@ packages: lru-memoizer: 2.3.0 transitivePeerDependencies: - supports-color - dev: false - /jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + jws@3.2.2: dependencies: jwa: 1.4.1 safe-buffer: 5.2.1 - /kafkajs@2.2.4: - resolution: {integrity: sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==} - engines: {node: '>=14.0.0'} + kafkajs@2.2.4: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false + kuler@2.0.0: {} - /lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} + lazystream@1.0.1: dependencies: readable-stream: 2.3.8 - dev: true - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - /limiter@1.1.5: - resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} - dev: false + limiter@1.1.5: {} - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lines-and-columns@1.2.4: {} - /local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} + local-pkg@0.5.0: dependencies: mlly: 1.7.1 pkg-types: 1.1.3 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - dev: false + lodash.clonedeep@4.5.0: {} - /lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - dev: true + lodash.defaults@4.2.0: {} - /lodash.difference@4.5.0: - resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - dev: true + lodash.difference@4.5.0: {} - /lodash.flatten@4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - dev: true + lodash.flatten@4.4.0: {} - /lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - dev: true + lodash.get@4.4.2: {} - /lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + lodash.includes@4.3.0: {} - /lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + lodash.isboolean@3.0.3: {} - /lodash.isempty@4.4.0: - resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==} - dev: false + lodash.isempty@4.4.0: {} - /lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - dev: false + lodash.isequal@4.5.0: {} - /lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + lodash.isinteger@4.0.4: {} - /lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + lodash.isnumber@3.0.3: {} - /lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + lodash.isplainobject@4.0.6: {} - /lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.isstring@4.0.1: {} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true + lodash.merge@4.6.2: {} - /lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + lodash.once@4.1.1: {} - /lodash.union@4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - dev: true + lodash.union@4.6.0: {} - /lodash.zip@4.2.0: - resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} - dev: true + lodash.zip@4.2.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true + lodash@4.17.21: {} - /logform@2.6.0: - resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} - engines: {node: '>= 12.0.0'} + logform@2.6.0: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -9236,245 +11831,129 @@ packages: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 - dev: false - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - dev: true - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lru-cache@10.4.0: - resolution: {integrity: sha512-bfJaPTuEiTYBu+ulDaeQ0F+uLmlfFkMgXj4cbwfuMSjgObGMzb55FMMbDvbRU0fAHZ4sLGkz2mKwcMg8Dvm8Ww==} - engines: {node: '>=18'} - dev: true + lru-cache@10.4.0: {} - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} + lru-cache@7.18.3: {} - /lru-memoizer@2.3.0: - resolution: {integrity: sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==} + lru-memoizer@2.3.0: dependencies: lodash.clonedeep: 4.5.0 lru-cache: 6.0.0 - dev: false - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + media-typer@0.3.0: {} - /media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - dev: false + memory-pager@1.5.0: {} - /memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false + meow@12.1.1: {} - /meow@12.1.1: - resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} - engines: {node: '>=16.10'} - dev: true + merge-descriptors@1.0.1: {} - /merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - dev: false + merge-descriptors@1.0.3: {} - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true + merge-stream@2.0.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge2@1.4.1: {} - /methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - dev: false + methods@1.1.2: {} - /micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: false + mime@1.6.0: {} - /mime@4.0.4: - resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} - engines: {node: '>=16'} - hasBin: true - dev: false + mime@4.0.4: {} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true + mimic-fn@4.0.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - dev: true - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimist@1.2.8: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mitt@3.0.1: {} - /mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - dev: true + mkdirp-classic@0.5.3: {} - /mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true + mkdirp@0.5.6: dependencies: minimist: 1.2.8 - dev: false - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@1.0.4: {} - /mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@3.0.1: {} - /mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 ufo: 1.5.3 - dev: true - /mnemonist@0.38.3: - resolution: {integrity: sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==} + mnemonist@0.38.3: dependencies: obliterator: 1.6.1 - /mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + mongodb-connection-string-url@3.0.1: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - dev: false - /mongodb@6.7.0: - resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true + mongodb@6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3): dependencies: '@mongodb-js/saslprep': 1.1.7 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - dev: false + optionalDependencies: + '@aws-sdk/credential-providers': 3.609.0 + socks: 2.8.3 - /ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - dev: false + ms@2.0.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /multer@1.4.5-lts.1: - resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==} - engines: {node: '>= 6.0.0'} + multer@1.4.5-lts.1: dependencies: append-field: 1.0.0 busboy: 1.6.0 @@ -9483,189 +11962,111 @@ packages: object-assign: 4.1.1 type-is: 1.6.18 xtend: 4.0.2 - dev: false - /nan@2.20.0: - resolution: {integrity: sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==} - requiresBuild: true + nan@2.20.0: optional: true - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true + natural-compare-lite@1.4.0: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - dev: true + natural-compare@1.4.0: {} - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: false + negotiator@0.6.3: {} - /neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + neo-async@2.6.2: {} - /netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} + netmask@2.0.2: {} - /nise@5.1.9: - resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} + nise@5.1.9: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 11.3.1 '@sinonjs/text-encoding': 0.7.3 just-extend: 6.2.0 path-to-regexp: 6.3.0 - dev: true - /node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - dev: true - /node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-releases@2.0.14: {} - /nodemailer@6.9.14: - resolution: {integrity: sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA==} - engines: {node: '>=6.0.0'} - dev: false + nodemailer@6.9.14: {} - /nodemailer@6.9.9: - resolution: {integrity: sha512-dexTll8zqQoVJEZPwQAKzxxtFn0qTnjdQTchoU6Re9BUUGBJiOy3YMn/0ShTW6J5M0dfQ1NeDeRTTl4oIWgQMA==} - engines: {node: '>=6.0.0'} - dev: false + nodemailer@6.9.9: {} - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + object-assign@4.1.1: {} - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} + object-inspect@1.13.2: {} - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true + object-keys@1.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - dev: true - /object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} + object.entries@1.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} + object.groupby@1.0.3: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 - dev: true - /object.hasown@1.1.4: - resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} - engines: {node: '>= 0.4'} + object.hasown@1.1.4: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /obliterator@1.6.1: - resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + obliterator@1.6.1: {} - /on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} + on-finished@2.4.1: dependencies: ee-first: 1.1.1 - dev: false - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + one-time@1.0.0: dependencies: fn.name: 1.1.0 - dev: false - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - dev: true - /openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + openapi-types@12.1.3: {} - /openapi-zod-client@1.18.1: - resolution: {integrity: sha512-L0GzU/7Sx9ugbWWoQwOJdKtyxr8ZnjxIK2RJP63//OkmKws2w7c5HSgS2bdNxPVCIp/eJuYk+CtaKfvCoJ08Yw==} - hasBin: true + openapi-zod-client@1.18.1: dependencies: '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3) '@liuli-util/fs-extra': 0.1.0 @@ -9687,14 +12088,11 @@ packages: - supports-color - xstate - /openapi3-ts@3.1.0: - resolution: {integrity: sha512-1qKTvCCVoV0rkwUh1zq5o8QyghmwYPuhdvtjv1rFjuOnJToXhQyF8eGjNETQ8QmGjr9Jz/tkAKLITIl2s7dw3A==} + openapi3-ts@3.1.0: dependencies: yaml: 2.4.5 - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: deep-is: 0.1.4 fast-levenshtein: 2.0.6 @@ -9702,37 +12100,22 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 word-wrap: 1.2.5 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-limit@5.0.0: - resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} - engines: {node: '>=18'} + p-limit@5.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@6.0.0: - resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} - engines: {node: '>=16'} - dev: true + p-map@6.0.0: {} - /pac-proxy-agent@7.0.2: - resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} - engines: {node: '>= 14'} + pac-proxy-agent@7.0.2: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 @@ -9745,48 +12128,27 @@ packages: transitivePeerDependencies: - supports-color - /pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} + pac-resolver@7.0.1: dependencies: degenerator: 5.0.1 netmask: 2.0.2 - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - dev: false + parseurl@1.3.3: {} - /pastable@2.2.1: - resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} - engines: {node: '>=14.x'} - peerDependencies: - react: '>=17' - xstate: '>=4.32.1' - peerDependenciesMeta: - react: - optional: true - xstate: - optional: true + pastable@2.2.1: dependencies: '@babel/core': 7.24.7 ts-toolbelt: 9.6.0 @@ -9794,88 +12156,49 @@ packages: transitivePeerDependencies: - supports-color - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-is-absolute@1.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + path-key@3.1.1: {} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true + path-key@4.0.0: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - dev: true + path-parse@1.0.7: {} - /path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.0 minipass: 7.1.2 - dev: true - /path-to-regexp@0.1.10: - resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} - dev: false + path-to-regexp@0.1.10: {} - /path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - dev: true + path-to-regexp@0.1.7: {} - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-to-regexp@6.3.0: {} - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true + path-type@4.0.0: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathe@1.1.2: {} - /pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pathval@1.1.1: {} - /pg-cloudflare@1.1.1: - resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} - requiresBuild: true + pend@1.2.0: {} + + pg-cloudflare@1.1.1: optional: true - /pg-connection-string@2.6.4: - resolution: {integrity: sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==} + pg-connection-string@2.6.4: {} - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} + pg-int8@1.0.1: {} - /pg-minify@1.6.4: - resolution: {integrity: sha512-cf6hBt1YqzqPX0OznXKSv4U7e4o7eUU4zp2zQsbJ+4OCNNr7EnnAVWkIz4k0dv6UN4ouS1ZL4WlXxCrZHHl69g==} - engines: {node: '>=14.0.0'} + pg-minify@1.6.4: {} - /pg-pool@3.6.2(pg@8.11.5): - resolution: {integrity: sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==} - peerDependencies: - pg: '>=8.0' + pg-pool@3.6.2(pg@8.11.5): dependencies: pg: 8.11.5 - /pg-promise@11.8.0: - resolution: {integrity: sha512-w9hTFpkM4FByJTJ7KCWLtZSOtQa2BKC+XIV8+3ZvDlfYfBYdz8V4V+BttnqhUPY/d12Itug7Bft4XdILihsY+w==} - engines: {node: '>=14.0'} + pg-promise@11.8.0: dependencies: assert-options: 0.8.1 pg: 8.11.5 @@ -9884,12 +12207,9 @@ packages: transitivePeerDependencies: - pg-native - /pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + pg-protocol@1.6.1: {} - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 @@ -9897,14 +12217,7 @@ packages: postgres-date: 1.0.7 postgres-interval: 1.2.0 - /pg@8.11.5: - resolution: {integrity: sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true + pg@8.11.5: dependencies: pg-connection-string: 2.6.4 pg-pool: 3.6.2(pg@8.11.5) @@ -9914,134 +12227,83 @@ packages: optionalDependencies: pg-cloudflare: 1.1.1 - /pgpass@1.0.5: - resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + pgpass@1.0.5: dependencies: split2: 4.2.0 - /picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.0.1: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picomatch@2.3.1: {} - /pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - dev: true + possible-typed-array-names@1.0.0: {} - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - dev: true - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} + postgres-array@2.0.0: {} - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} + postgres-bytea@1.0.0: {} - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} + postgres-date@1.0.7: {} - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true + prelude-ls@1.2.1: {} - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} + prettier-linter-helpers@1.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 + prettier@2.8.8: {} - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - dev: true - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-nextick-args@2.0.1: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} + progress@2.0.3: {} - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 retry: 0.12.0 - dev: false - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - dev: true - /proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 retry: 0.12.0 signal-exit: 3.0.7 - dev: true - /properties-reader@2.3.0: - resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==} - engines: {node: '>=14'} + properties-reader@2.3.0: dependencies: mkdirp: 1.0.4 - dev: true - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - dev: false - /proxy-agent@6.4.0: - resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} - engines: {node: '>= 14'} + proxy-agent@6.4.0: dependencies: agent-base: 7.1.1 debug: 4.3.5 @@ -10054,22 +12316,16 @@ packages: transitivePeerDependencies: - supports-color - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@1.1.0: {} - /pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + pump@3.0.0: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /puppeteer-core@22.11.2: - resolution: {integrity: sha512-vQo+YDuePyvj+92Z9cdtxi/HalKf+k/R4tE80nGtQqJRNqU81eHaHkbVfnLszdaLlvwFF5tipnnSCzqWlEddtw==} - engines: {node: '>=18'} + puppeteer-core@22.11.2: dependencies: '@puppeteer/browsers': 2.2.3 chromium-bidi: 0.5.23(devtools-protocol@0.0.1299070) @@ -10081,11 +12337,7 @@ packages: - supports-color - utf-8-validate - /puppeteer@22.11.2(typescript@5.4.5): - resolution: {integrity: sha512-8fjdQSgW0sq7471ftca24J7sXK+jXZ7OW7Gx+NEBFNyXrcTiBfukEI46gNq6hiMhbLEDT30NeylK/1ZoPdlKSA==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + puppeteer@22.11.2(typescript@5.4.5): dependencies: '@puppeteer/browsers': 2.2.3 cosmiconfig: 9.0.0(typescript@5.4.5) @@ -10097,75 +12349,45 @@ packages: - typescript - utf-8-validate - /qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} + qs@6.11.0: dependencies: side-channel: 1.0.6 - dev: false - /qs@6.12.3: - resolution: {integrity: sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==} - engines: {node: '>=0.6'} + qs@6.12.3: dependencies: side-channel: 1.0.6 - dev: false - /qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} + qs@6.13.0: dependencies: side-channel: 1.0.6 - dev: false - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /queue-tick@1.0.1: - resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + queue-tick@1.0.1: {} - /quote@0.4.0: - resolution: {integrity: sha512-KHp3y3xDjuBhRx+tYKOgzPnVHMRlgpn2rU450GcU4PL24r1H6ls/hfPrxDwX2pvYMlwODHI2l8WwgoV69x5rUQ==} - dev: true + quote@0.4.0: {} - /randexp@0.5.3: - resolution: {integrity: sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==} - engines: {node: '>=4'} + randexp@0.5.3: dependencies: drange: 1.1.1 ret: 0.2.2 - dev: true - /range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - dev: false + range-parser@1.2.1: {} - /rate-limiter-flexible@5.0.3: - resolution: {integrity: sha512-lWx2y8NBVlTOLPyqs+6y7dxfEpT6YFqKy3MzWbCy95sTTOhOuxufP2QvRyOHpfXpB9OUJPbVLybw3z3AVAS5fA==} - dev: false + rate-limiter-flexible@5.0.3: {} - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} + raw-body@2.5.2: dependencies: bytes: 3.1.2 http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: false - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true + react-is@16.13.1: {} - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true + react-is@18.3.1: {} - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -10175,22 +12397,17 @@ packages: string_decoder: 1.1.1 util-deprecate: 1.0.2 - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdir-glob@1.1.3: dependencies: minimatch: 5.1.6 - dev: true - /redis@4.6.15: - resolution: {integrity: sha512-2NtuOpMW3tnYzBw6S8mbXSX7RPzvVFCA2wFJq9oErushO2UeBkxObk+uvo7gv7n0rhWeOj/IzrHO8TjcFlRSOg==} + redis@4.6.15: dependencies: '@redis/bloom': 1.2.0(@redis/client@1.5.17) '@redis/client': 1.5.17 @@ -10198,11 +12415,8 @@ packages: '@redis/json': 1.0.6(@redis/client@1.5.17) '@redis/search': 1.1.6(@redis/client@1.5.17) '@redis/time-series': 1.0.5(@redis/client@1.5.17) - dev: false - /reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} - engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -10211,83 +12425,47 @@ packages: get-intrinsic: 1.2.4 globalthis: 1.0.4 which-builtin-type: 1.1.3 - dev: true - /regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - dev: true - /req-all@0.1.0: - resolution: {integrity: sha512-ZdvPr8uXy9ujX3KujwE2P1HWkMYgogIhqeAeyb47MqWjSfyxERSm0TNbN/IapCCmWDufXab04AYrRgObaJCJ6Q==} - engines: {node: '>=4'} - dev: true + req-all@0.1.0: {} - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@4.0.0: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: true + resolve-pkg-maps@1.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: true - /ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - dev: true + ret@0.2.2: {} - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} + retry@0.12.0: {} - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true + reusify@1.0.4: {} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -10308,66 +12486,41 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.18.1 '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 - dev: true - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - dev: true - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 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 + safe-stable-stringify@2.4.3: {} - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + safer-buffer@2.1.2: {} - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true + semver@6.3.1: {} - /semver@7.6.0: - resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.0: dependencies: lru-cache: 6.0.0 - /semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.2: {} - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + send@0.18.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -10384,11 +12537,8 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -10405,11 +12555,8 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /serve-static@1.16.0: - resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} - engines: {node: '>= 0.8.0'} + serve-static@1.15.0: dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 @@ -10417,11 +12564,17 @@ packages: send: 0.18.0 transitivePeerDependencies: - supports-color - dev: false - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} + serve-static@1.16.0: + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.18.0 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -10430,66 +12583,41 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.2 - /set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 - dev: true - /setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - dev: false + setprototypeof@1.2.0: {} - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - dev: true - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} + side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 object-inspect: 1.13.2 - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + siginfo@2.0.0: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true + signal-exit@4.1.0: {} - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - dev: false - /sinon@16.1.3: - resolution: {integrity: sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA==} + sinon@16.1.3: dependencies: '@sinonjs/commons': 3.0.1 '@sinonjs/fake-timers': 10.3.0 @@ -10497,25 +12625,14 @@ packages: diff: 5.2.0 nise: 5.1.9 supports-color: 7.2.0 - dev: true - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /slash@4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} - dev: true + slash@4.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + smart-buffer@4.2.0: {} - /socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} - engines: {node: '>= 14'} + socks-proxy-agent@8.0.4: dependencies: agent-base: 7.1.1 debug: 4.3.5 @@ -10523,81 +12640,50 @@ packages: transitivePeerDependencies: - supports-color - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true + source-map-js@1.2.0: {} - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} + source-map@0.6.1: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 - dev: true - /spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - dev: true + spdx-license-ids@3.0.18: {} - /spex@3.3.0: - resolution: {integrity: sha512-VNiXjFp6R4ldPbVRYbpxlD35yRHceecVXlct1J4/X80KuuPnW2AXMq3sGwhnJOhKkUsOxAT6nRGfGE5pocVw5w==} - engines: {node: '>=10.0.0'} + spex@3.3.0: {} - /split-ca@1.0.1: - resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} - dev: true + split-ca@1.0.1: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sprintf-js@1.0.3: {} - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sprintf-js@1.1.3: {} - /ssh-remote-port-forward@1.0.4: - resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + ssh-remote-port-forward@1.0.4: dependencies: '@types/ssh2': 0.5.52 ssh2: 1.15.0 - dev: true - /ssh2-sftp-client@9.1.0: - resolution: {integrity: sha512-Hzdr9OE6GxZjcmyM9tgBSIFVyrHAp9c6U2Y4yBkmYOHoQvZ7pIm27dmltvcmRfxcWiIcg8HBvG5iAikDf+ZuzQ==} - engines: {node: '>=10.24.1'} + ssh2-sftp-client@9.1.0: dependencies: concat-stream: 2.0.0 promise-retry: 2.0.1 ssh2: 1.15.0 - dev: false - /ssh2@1.15.0: - resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==} - engines: {node: '>=10.16.0'} - requiresBuild: true + ssh2@1.15.0: dependencies: asn1: 0.2.6 bcrypt-pbkdf: 1.0.2 @@ -10605,34 +12691,19 @@ packages: cpu-features: 0.0.10 nan: 2.20.0 - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false + stack-trace@0.0.10: {} - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + stackback@0.0.2: {} - /statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - dev: false + statuses@2.0.1: {} - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true + std-env@3.7.0: {} - /stream-transform@3.3.3: - resolution: {integrity: sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==} - dev: false + stream-transform@3.3.3: {} - /streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - dev: false + streamsearch@1.1.0: {} - /streamx@2.18.0: - resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} + streamx@2.18.0: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 @@ -10640,26 +12711,19 @@ packages: optionalDependencies: bare-events: 2.4.2 - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} - engines: {node: '>= 0.4'} + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -10673,122 +12737,81 @@ packages: regexp.prototype.flags: 1.5.2 set-function-name: 2.0.2 side-channel: 1.0.6 - dev: true - /string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: true - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true + strip-final-newline@3.0.0: {} - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: true + strip-json-comments@3.1.1: {} - /strip-literal@2.1.0: - resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + strip-literal@2.1.0: dependencies: js-tokens: 9.0.0 - dev: true - /strnum@1.0.5: - resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + strnum@1.0.5: {} - /subarg@1.0.0: - resolution: {integrity: sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==} + subarg@1.0.0: dependencies: minimist: 1.2.8 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - dev: true - - /tanu@0.1.13: - resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} + supports-preserve-symlinks-flag@1.0.0: {} + + tanu@0.1.13: dependencies: tslib: 2.6.3 typescript: 4.9.5 - /tar-fs@2.0.1: - resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==} + tar-fs@2.0.1: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 pump: 3.0.0 tar-stream: 2.2.0 - dev: true - /tar-fs@3.0.5: - resolution: {integrity: sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg==} + tar-fs@3.0.5: dependencies: pump: 3.0.0 tar-stream: 3.1.7 @@ -10796,36 +12819,29 @@ packages: bare-fs: 2.3.1 bare-path: 2.1.3 - /tar-fs@3.0.6: - resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + tar-fs@3.0.6: dependencies: pump: 3.0.0 tar-stream: 3.1.7 optionalDependencies: bare-fs: 2.3.1 bare-path: 2.1.3 - dev: true - /tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} + tar-stream@2.2.0: dependencies: bl: 4.1.0 end-of-stream: 1.4.4 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + tar-stream@3.1.7: dependencies: b4a: 1.6.6 fast-fifo: 1.3.2 streamx: 2.18.0 - /testcontainers@10.9.0: - resolution: {integrity: sha512-LN+cKAOd61Up9SVMJW+3VFVGeVQG8JBqZhEQo2U0HBfIsAynyAXcsLBSo+KZrOfy9SBz7pGHctWN/KabLDbNFA==} + testcontainers@10.9.0: dependencies: '@balena/dockerignore': 1.0.2 '@types/dockerode': 3.3.29 @@ -10845,116 +12861,46 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true - /text-decoder@1.1.1: - resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + text-decoder@1.1.1: dependencies: b4a: 1.6.6 - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false + text-hex@1.0.0: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true + text-table@0.2.0: {} - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + through@2.3.8: {} - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - dev: true + tinybench@2.8.0: {} - /tinypool@0.8.4: - resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} - engines: {node: '>=14.0.0'} - dev: true + tinypool@0.8.4: {} - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true + tinyspy@2.2.1: {} - /tmp@0.2.3: - resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} - engines: {node: '>=14.14'} - dev: true + tmp@0.2.3: {} - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + to-fast-properties@2.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - dev: false + toidentifier@1.0.1: {} - /tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true + tr46@0.0.3: {} - /tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} + tr46@4.1.1: dependencies: punycode: 2.3.1 - dev: false - - /triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - dev: false - /ts-node@10.9.2(@types/node@20.14.6)(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.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.6 - acorn: 8.12.1 - acorn-walk: 8.3.3 - 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 + triple-beam@1.4.1: {} - /ts-pattern@5.2.0: - resolution: {integrity: sha512-aGaSpOlDcns7ZoeG/OMftWyQG1KqPVhgplhJxNCvyIXqWrumM5uIoOSarw/hmmi/T1PnuQ/uD8NaFHvLpHicDg==} + ts-pattern@5.2.0: {} - /ts-toolbelt@9.6.0: - resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} + ts-toolbelt@9.6.0: {} - /tsc-esm-fix@2.20.27: - resolution: {integrity: sha512-bfoSY29XN4yRvXgfxc4rtKQPe9Xx02BahWSZ3D4GgBXIWSE+TJ/BXGSrpUIBkrsKIUQv2zA3qiwJVFnUV59Xdw==} - engines: {node: '>=16.0.0'} - hasBin: true + tsc-esm-fix@2.20.27: dependencies: depseek: 0.4.1 fs-extra: 11.2.0 @@ -10962,164 +12908,91 @@ packages: json5: 2.2.3 meow: 12.1.1 tslib: 2.6.3 - dev: true - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: 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==} + tslib@1.14.1: {} - /tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.6.3: {} - /tsutils@3.21.0(typescript@5.4.5): - 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' + tsutils@3.21.0(typescript@5.4.5): dependencies: tslib: 1.14.1 typescript: 5.4.5 - dev: true - /tsx@4.19.1: - resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} - engines: {node: '>=18.0.0'} - hasBin: true + tsx@4.19.1: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.1 optionalDependencies: fsevents: 2.3.3 - dev: true - /turbo-darwin-64@2.0.4: - resolution: {integrity: sha512-x9mvmh4wudBstML8Z8IOmokLWglIhSfhQwnh2gBCSqabgVBKYvzl8Y+i+UCNPxheCGTgtsPepTcIaKBIyFIcvw==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-64@2.2.3: optional: true - /turbo-darwin-arm64@2.0.4: - resolution: {integrity: sha512-/B1Ih8zPRGVw5vw4SlclOf3C/woJ/2T6ieH6u54KT4wypoaVyaiyMqBcziIXycdObIYr7jQ+raHO7q3mhay9/A==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + turbo-darwin-arm64@2.2.3: optional: true - /turbo-linux-64@2.0.4: - resolution: {integrity: sha512-6aG670e5zOWu6RczEYcB81nEl8EhiGJEvWhUrnAfNEUIMBEH1pR5SsMmG2ol5/m3PgiRM12r13dSqTxCLcHrVg==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-64@2.2.3: optional: true - /turbo-linux-arm64@2.0.4: - resolution: {integrity: sha512-AXfVOjst+mCtPDFT4tCu08Qrfv12Nj7NDd33AjGwV79NYN1Y1rcFY59UQ4nO3ij3rbcvV71Xc+TZJ4csEvRCSg==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + turbo-linux-arm64@2.2.3: optional: true - /turbo-windows-64@2.0.4: - resolution: {integrity: sha512-QOnUR9hKl0T5gq5h1fAhVEqBSjpcBi/BbaO71YGQNgsr6pAnCQdbG8/r3MYXet53efM0KTdOhieWeO3KLNKybA==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-64@2.2.3: optional: true - /turbo-windows-arm64@2.0.4: - resolution: {integrity: sha512-3v8WpdZy1AxZw0gha0q3caZmm+0gveBQ40OspD6mxDBIS+oBtO5CkxhIXkFJJW+jDKmDlM7wXDIGfMEq+QyNCQ==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + turbo-windows-arm64@2.2.3: optional: true - /turbo@2.0.4: - resolution: {integrity: sha512-Ilme/2Q5kYw0AeRr+aw3s02+WrEYaY7U8vPnqSZU/jaDG/qd6jHVN6nRWyd/9KXvJGYM69vE6JImoGoyNjLwaw==} - hasBin: true + turbo@2.2.3: optionalDependencies: - turbo-darwin-64: 2.0.4 - turbo-darwin-arm64: 2.0.4 - turbo-linux-64: 2.0.4 - turbo-linux-arm64: 2.0.4 - turbo-windows-64: 2.0.4 - turbo-windows-arm64: 2.0.4 - dev: true - - /tweetnacl@0.14.5: - resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + turbo-darwin-64: 2.2.3 + turbo-darwin-arm64: 2.2.3 + turbo-linux-64: 2.2.3 + turbo-linux-arm64: 2.2.3 + turbo-windows-64: 2.2.3 + turbo-windows-arm64: 2.2.3 - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + tweetnacl@0.14.5: {} + + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - dev: true + type-detect@4.1.0: {} - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true + type-fest@0.20.2: {} - /type-fest@3.13.1: - resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} - engines: {node: '>=14.16'} + type-fest@3.13.1: {} - /type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} + type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - dev: false - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -11127,11 +13000,8 @@ packages: gopd: 1.0.1 has-proto: 1.0.3 is-typed-array: 1.1.13 - dev: true - /typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -11139,113 +13009,59 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - dev: true - /typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - dev: false + typedarray@0.0.6: {} - /typescript@3.9.10: - resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: false + typescript@3.9.10: {} - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true + typescript@4.9.5: {} - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true + typescript@5.4.5: {} - /ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - dev: true + ufo@1.5.3: {} - /uglify-js@3.18.0: - resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==} - engines: {node: '>=0.8.0'} - hasBin: true - requiresBuild: true + uglify-js@3.18.0: optional: true - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - dev: true - /unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + unbzip2-stream@1.4.3: dependencies: buffer: 5.7.1 through: 2.3.8 - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@5.26.5: {} - /universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} + universalify@2.0.1: {} - /unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - dev: false + unpipe@1.0.0: {} - /update-browserslist-db@1.1.0(browserslist@4.23.1): - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0(browserslist@4.23.1): dependencies: browserslist: 4.23.1 escalade: 3.1.2 picocolors: 1.0.1 - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} - - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - /utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - dev: false + urlpattern-polyfill@10.0.0: {} - /uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true + util-deprecate@1.0.2: {} - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true + utils-merge@1.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true + uuid@9.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vite-node@1.6.0(@types/node@20.14.6): - resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true + vite-node@1.6.0(@types/node@20.14.6): dependencies: cac: 6.7.14 debug: 4.3.5 @@ -11261,70 +13077,18 @@ packages: - sugarss - supports-color - terser - dev: true - /vite@5.3.3(@types/node@20.14.6): - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - 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 + vite@5.3.3(@types/node@20.14.6): dependencies: - '@types/node': 20.14.6 esbuild: 0.21.5 postcss: 8.4.39 rollup: 4.18.1 optionalDependencies: + '@types/node': 20.14.6 fsevents: 2.3.3 - dev: true - /vitest@1.6.0(@types/node@20.14.6): - 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 + vitest@1.6.0(@types/node@20.14.6): dependencies: - '@types/node': 20.14.6 '@vitest/expect': 1.6.0 '@vitest/runner': 1.6.0 '@vitest/snapshot': 1.6.0 @@ -11345,6 +13109,8 @@ packages: vite: 5.3.3(@types/node@20.14.6) vite-node: 1.6.0(@types/node@20.14.6) why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.14.6 transitivePeerDependencies: - less - lightningcss @@ -11353,52 +13119,35 @@ packages: - sugarss - supports-color - terser - dev: true - /webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true + webidl-conversions@3.0.1: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} + whatwg-url@13.0.0: dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true - /whence@2.0.1: - resolution: {integrity: sha512-VtcCE1Pe3BKofF/k+P5xcpuoqQ0f1NJY6TmdUw5kInl9/pEr1ZEFD9+ZOUicf52tvpTbhMS93aWXriu2IQYTTw==} - engines: {node: '>=14'} + whence@2.0.1: dependencies: '@babel/parser': 7.24.7 eval-estree-expression: 2.0.0 - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: 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-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} + which-builtin-type@1.1.3: dependencies: function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 @@ -11412,58 +13161,38 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: true - /which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} + which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.3 - dev: true - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.0.1 has-tostringtag: 1.0.2 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true - /winston-transport@4.7.0: - resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} - engines: {node: '>= 12.0.0'} + winston-transport@4.7.0: dependencies: logform: 2.6.0 readable-stream: 3.6.2 triple-beam: 1.4.1 - dev: false - /winston@3.13.0: - resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} - engines: {node: '>= 12.0.0'} + winston@3.13.0: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 @@ -11476,74 +13205,40 @@ packages: stack-trace: 0.0.10 triple-beam: 1.4.1 winston-transport: 4.7.0 - dev: false - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - dev: true + word-wrap@1.2.5: {} - /wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wordwrap@1.0.0: {} - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrappy@1.0.2: {} - /ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.17.1: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + y18n@5.0.8: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} - engines: {node: '>= 14'} - hasBin: true + yaml@2.4.5: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} + yargs-parser@21.1.1: {} - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -11553,44 +13248,23 @@ packages: y18n: 5.0.8 yargs-parser: 21.1.1 - /yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 fd-slicer: 1.1.0 - /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 + yocto-queue@0.1.0: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@1.1.1: {} - /zip-stream@4.1.1: - resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} - engines: {node: '>= 10'} + zip-stream@4.1.1: dependencies: archiver-utils: 3.0.4 compress-commons: 4.1.2 readable-stream: 3.6.2 - dev: true - /zod-validation-error@3.3.0(zod@3.23.8): - resolution: {integrity: sha512-Syib9oumw1NTqEv4LT0e6U83Td9aVRk9iTXPUQr1otyV1PuXQKOvOwhMNqZIq5hluzHP2pMgnOmHEo7kPdI2mw==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.18.0 + zod-validation-error@3.3.0(zod@3.23.8): dependencies: zod: 3.23.8 - dev: false - /zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.23.8: {} From 107afd7979b4cd780ec019e8f607c086c8f9206b Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Wed, 11 Dec 2024 15:37:44 +0100 Subject: [PATCH 076/126] Update updateDescriptorAttributes.test.ts --- .../test/updateDescriptorAttributes.test.ts | 224 +++++++++--------- 1 file changed, 114 insertions(+), 110 deletions(-) diff --git a/packages/catalog-process/test/updateDescriptorAttributes.test.ts b/packages/catalog-process/test/updateDescriptorAttributes.test.ts index bd338136be..285a3d13cf 100644 --- a/packages/catalog-process/test/updateDescriptorAttributes.test.ts +++ b/packages/catalog-process/test/updateDescriptorAttributes.test.ts @@ -3,6 +3,7 @@ import { genericLogger } from "pagopa-interop-commons"; import { decodeProtobufPayload, getMockAttribute, + getMockDelegation, } from "pagopa-interop-commons-test/index.js"; import { Descriptor, @@ -14,6 +15,8 @@ import { EServiceDescriptorAttributesUpdatedV2, operationForbidden, AttributeId, + delegationKind, + delegationState, } from "pagopa-interop-models"; import { catalogApi } from "pagopa-interop-api-clients"; import { expect, describe, it, beforeEach } from "vitest"; @@ -23,11 +26,12 @@ import { eServiceNotFound, inconsistentAttributesSeedGroupsCount, descriptorAttributeGroupSupersetMissingInAttributesSeed, - notValidDescriptor, + notValidDescriptorState, unchangedAttributes, } from "../src/model/domain/errors.js"; import { addOneAttribute, + addOneDelegation, addOneEService, catalogService, getMockAuthData, @@ -169,72 +173,72 @@ describe("update descriptor", () => { } ); - // it.each([descriptorState.published, descriptorState.suspended])( - // "should write on event-store for the attributes update of a descriptor with state %s (producer delegate)", - // async (descriptorState) => { - // const mockDescriptor: Descriptor = { - // ...getMockDescriptor(), - // state: descriptorState, - // attributes: { - // certified: validMockDescriptorCertifiedAttributes, - // verified: validMockDescriptorVerifiedAttributes, - // declared: [], - // }, - // }; - - // const mockEService: EService = { - // ...getMockEService(), - // descriptors: [mockDescriptor], - // }; - - // await addOneEService(mockEService); - - // const delegation = getMockDelegation({ - // kind: delegationKind.delegatedProducer, - // eserviceId: mockEService.id, - // state: delegationState.active, - // }); - - // await addOneDelegation(delegation); - - // const updatedEService: EService = { - // ...mockEService, - // descriptors: [ - // { - // ...mockDescriptor, - // attributes: - // validMockDescriptorAttributeSeed as Descriptor["attributes"], - // }, - // ], - // }; - - // const returnedEService = await catalogService.updateDescriptorAttributes( - // mockEService.id, - // mockDescriptor.id, - // validMockDescriptorAttributeSeed, - // { - // authData: getMockAuthData(delegation.delegateId), - // correlationId: generateId(), - // serviceName: "", - // logger: genericLogger, - // } - // ); - - // const writtenEvent = await readLastEserviceEvent(mockEService.id); - // expect(writtenEvent).toMatchObject({ - // stream_id: mockEService.id, - // version: "1", - // type: "EServiceDescriptorAttributesUpdated", - // event_version: 2, - // }); - // const writtenPayload = decodeProtobufPayload({ - // messageType: EServiceDescriptorAttributesUpdatedV2, - // payload: writtenEvent.data, - // }); - // expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); - // expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); - // } - // ); + it.each([descriptorState.published, descriptorState.suspended])( + "should write on event-store for the attributes update of a descriptor with state %s (producer delegate)", + async (descriptorState) => { + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState, + attributes: { + certified: validMockDescriptorCertifiedAttributes, + verified: validMockDescriptorVerifiedAttributes, + declared: [], + }, + }; + + const mockEService: EService = { + ...getMockEService(), + descriptors: [mockDescriptor], + }; + + await addOneEService(mockEService); + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneDelegation(delegation); + + const updatedEService: EService = { + ...mockEService, + descriptors: [ + { + ...mockDescriptor, + attributes: + validMockDescriptorAttributeSeed as Descriptor["attributes"], + }, + ], + }; + + const returnedEService = await catalogService.updateDescriptorAttributes( + mockEService.id, + mockDescriptor.id, + validMockDescriptorAttributeSeed, + { + authData: getMockAuthData(delegation.delegateId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ); + + const writtenEvent = await readLastEserviceEvent(mockEService.id); + expect(writtenEvent).toMatchObject({ + stream_id: mockEService.id, + version: "1", + type: "EServiceDescriptorAttributesUpdated", + event_version: 2, + }); + const writtenPayload = decodeProtobufPayload({ + messageType: EServiceDescriptorAttributesUpdatedV2, + payload: writtenEvent.data, + }); + expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); + expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); + } + ); it("should throw eServiceNotFound if the eservice doesn't exist", async () => { const mockDescriptor: Descriptor = { @@ -387,54 +391,54 @@ describe("update descriptor", () => { ).rejects.toThrowError(operationForbidden); }); - // it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { - // const mockDescriptor: Descriptor = { - // ...getMockDescriptor(), - // state: descriptorState.published, - // attributes: { - // certified: validMockDescriptorCertifiedAttributes, - // verified: validMockDescriptorVerifiedAttributes, - // declared: [], - // }, - // }; - - // const mockEService: EService = { - // ...getMockEService(), - // descriptors: [mockDescriptor], - // }; - - // await addOneEService(mockEService); - - // const delegation = getMockDelegation({ - // kind: delegationKind.delegatedProducer, - // eserviceId: mockEService.id, - // state: delegationState.active, - // }); - - // await addOneDelegation(delegation); - - // expect( - // catalogService.updateDescriptorAttributes( - // mockEService.id, - // mockDescriptor.id, - // validMockDescriptorAttributeSeed, - // { - // authData: getMockAuthData(mockEService.producerId), - // correlationId: generateId(), - // serviceName: "", - // logger: genericLogger, - // } - // ) - // ).rejects.toThrowError(operationForbidden); - // }); + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + const mockDescriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + attributes: { + certified: validMockDescriptorCertifiedAttributes, + verified: validMockDescriptorVerifiedAttributes, + declared: [], + }, + }; + + const mockEService: EService = { + ...getMockEService(), + descriptors: [mockDescriptor], + }; + + await addOneEService(mockEService); + + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneDelegation(delegation); + + expect( + catalogService.updateDescriptorAttributes( + mockEService.id, + mockDescriptor.id, + validMockDescriptorAttributeSeed, + { + authData: getMockAuthData(mockEService.producerId), + correlationId: generateId(), + serviceName: "", + logger: genericLogger, + } + ) + ).rejects.toThrowError(operationForbidden); + }); it.each([ descriptorState.draft, - // descriptorState.waitingForApproval, + descriptorState.waitingForApproval, descriptorState.archived, descriptorState.deprecated, ])( - "should throw notValidDescriptor if the descriptor is in %s state", + "should throw notValidDescriptorState if the descriptor is in %s state", async (descriptorState) => { const mockDescriptor: Descriptor = { ...getMockDescriptor(), @@ -466,7 +470,7 @@ describe("update descriptor", () => { } ) ).rejects.toThrowError( - notValidDescriptor(mockDescriptor.id, descriptorState) + notValidDescriptorState(mockDescriptor.id, descriptorState) ); } ); From 0af49bdbc7f3753f76fae737feeb00fe802859f1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 11 Dec 2024 18:13:01 +0100 Subject: [PATCH 077/126] IMN-915 Improve logging in authorization-server and platformstate-writers (#1208) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 31 ++++++-- .../src/consumerServiceV2.ts | 6 +- .../src/index.ts | 8 +- .../src/utils.ts | 11 +++ .../consumerServiceV1.integration.test.ts | 35 ++++----- .../consumerServiceV2.integration.test.ts | 55 +++++++------- .../test/utils.test.ts | 3 + .../src/consumerServiceV1.ts | 19 +++-- .../src/consumerServiceV2.ts | 19 +++-- .../src/index.ts | 8 +- .../src/utils.ts | 8 +- .../consumerServiceV1.integration.test.ts | 45 +++++------ .../consumerServiceV2.integration.test.ts | 39 +++++----- .../test/utils.test.ts | 4 +- .../src/model/domain/errors.ts | 19 +++-- .../src/routers/AuthorizationServerRouter.ts | 37 ++++++++- .../src/services/tokenService.ts | 75 +++++++++++++++++-- .../authorizationServer.integration.test.ts | 46 +++++++++--- .../client-assertion-validation/src/index.ts | 1 + packages/models/src/errors.ts | 2 +- .../src/consumerServiceV1.ts | 7 +- .../src/consumerServiceV2.ts | 7 +- .../purpose-platformstate-writer/src/index.ts | 8 +- .../purpose-platformstate-writer/src/utils.ts | 63 ++++++++++++---- .../consumerServiceV1.integration.test.ts | 22 +++--- .../consumerServiceV2.integration.test.ts | 51 ++++++------- .../test/utils.test.ts | 13 +++- 27 files changed, 442 insertions(+), 200 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 7e69f31097..e194dd5076 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -14,6 +14,7 @@ import { PlatformStatesCatalogEntry, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { Logger } from "pagopa-interop-commons"; import { readAgreementEntry, updateAgreementStateInPlatformStatesEntry, @@ -28,19 +29,26 @@ import { export async function handleMessageV1( message: AgreementEventEnvelopeV1, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with({ type: "AgreementActivated" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); - await handleFirstActivation(agreement, dynamoDBClient, msg.version); + await handleFirstActivation( + agreement, + dynamoDBClient, + msg.version, + logger + ); }) .with({ type: "AgreementSuspended" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); await handleActivationOrSuspension( agreement, dynamoDBClient, - msg.version + msg.version, + logger ); }) .with({ type: "AgreementUpdated" }, async (msg) => { @@ -53,7 +61,8 @@ export async function handleMessageV1( await handleActivationOrSuspension( agreement, dynamoDBClient, - msg.version + msg.version, + logger ); }) .with(agreementState.archived, async () => { @@ -77,7 +86,7 @@ export async function handleMessageV1( .with(agreementState.active, async () => { // this case is for agreement upgraded const agreement = parseAgreement(msg.data.agreement); - await handleUpgrade(agreement, dynamoDBClient, msg.version); + await handleUpgrade(agreement, dynamoDBClient, msg.version, logger); }) .with( agreementState.draft, @@ -113,7 +122,8 @@ const parseAgreement = (agreementV1: AgreementV1 | undefined): Agreement => { const handleFirstActivation = async ( agreement: Agreement, dynamoDBClient: DynamoDBClient, - incomingVersion: number + incomingVersion: number, + logger: Logger ): Promise => { const primaryKey = makePlatformStatesAgreementPK(agreement.id); @@ -181,6 +191,7 @@ const handleFirstActivation = async ( dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }); } }; @@ -188,7 +199,8 @@ const handleFirstActivation = async ( const handleActivationOrSuspension = async ( agreement: Agreement, dynamoDBClient: DynamoDBClient, - incomingVersion: number + incomingVersion: number, + logger: Logger ): Promise => { const primaryKey = makePlatformStatesAgreementPK(agreement.id); @@ -241,6 +253,7 @@ const handleActivationOrSuspension = async ( dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }); } }; @@ -277,7 +290,8 @@ const handleArchiving = async ( const handleUpgrade = async ( agreement: Agreement, dynamoDBClient: DynamoDBClient, - msgVersion: number + msgVersion: number, + logger: Logger ): Promise => { const primaryKey = makePlatformStatesAgreementPK(agreement.id); const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); @@ -343,6 +357,7 @@ const handleUpgrade = async ( dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }); } }; diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index e7698d371b..e71bfea41a 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -13,6 +13,7 @@ import { PlatformStatesCatalogEntry, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { Logger } from "pagopa-interop-commons"; import { agreementStateToItemState, deleteAgreementEntry, @@ -27,7 +28,8 @@ import { export async function handleMessageV2( message: AgreementEventEnvelopeV2, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with({ type: "AgreementActivated" }, async (msg) => { @@ -104,6 +106,7 @@ export async function handleMessageV2( dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }); } }) @@ -229,6 +232,7 @@ export async function handleMessageV2( dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }); } }; diff --git a/packages/agreement-platformstate-writer/src/index.ts b/packages/agreement-platformstate-writer/src/index.ts index a0466dd8b3..37bf0f6c07 100644 --- a/packages/agreement-platformstate-writer/src/index.ts +++ b/packages/agreement-platformstate-writer/src/index.ts @@ -31,8 +31,12 @@ async function processMessage({ }); await match(decodedMessage) - .with({ event_version: 1 }, (msg) => handleMessageV1(msg, dynamoDBClient)) - .with({ event_version: 2 }, (msg) => handleMessageV2(msg, dynamoDBClient)) + .with({ event_version: 1 }, (msg) => + handleMessageV1(msg, dynamoDBClient, loggerInstance) + ) + .with({ event_version: 2 }, (msg) => + handleMessageV2(msg, dynamoDBClient, loggerInstance) + ) .exhaustive(); loggerInstance.info( diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index dac5b945d5..cee6715880 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -32,6 +32,7 @@ import { } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; +import { Logger } from "pagopa-interop-commons"; import { config } from "./config/config.js"; export const writeAgreementEntry = async ( @@ -196,6 +197,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }: { entriesToUpdate: TokenGenStatesConsumerClientGSIAgreement[]; agreementId: AgreementId; @@ -203,6 +205,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = dynamoDBClient: DynamoDBClient; GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; catalogEntry: PlatformStatesCatalogEntry | undefined; + logger: Logger; }): Promise => { for (const entry of entriesToUpdate) { const additionalDescriptorInfo = @@ -211,6 +214,11 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = !entry.descriptorAudience || !entry.descriptorVoucherLifespan); + if (additionalDescriptorInfo) { + logger.info( + `Adding descriptor info to token-generation-states entry with PK ${entry.PK} and GSIPK_eserviceId_descriptorId ${GSIPK_eserviceId_descriptorId}` + ); + } const additionalAttributesToSet: Record = additionalDescriptorInfo ? { @@ -336,6 +344,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }: { GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; agreementId: AgreementId; @@ -343,6 +352,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ dynamoDBClient: DynamoDBClient; GSIPK_eserviceId_descriptorId: GSIPKEServiceIdDescriptorId; catalogEntry: PlatformStatesCatalogEntry | undefined; + logger: Logger; }): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, @@ -389,6 +399,7 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger, }); if (!data.LastEvaluatedKey) { diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 123f6e3463..6eb6bdeb55 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -41,6 +41,7 @@ import { writeTokenGenStatesConsumerClient, readAllTokenGenStatesItems, } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { dynamoDBClient } from "./utils.js"; @@ -133,7 +134,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -229,7 +230,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -345,7 +346,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -489,7 +490,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -673,7 +674,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -796,7 +797,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -946,7 +947,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( latestAgreementEntryPrimaryKey, @@ -1120,7 +1121,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( previousAgreementEntryPrimaryKey, @@ -1235,7 +1236,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -1321,7 +1322,7 @@ describe("integration tests V1 events", async () => { agreement.id ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -1442,7 +1443,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -1589,7 +1590,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -1660,7 +1661,7 @@ describe("integration tests V1 events", async () => { agreement.id ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -1781,7 +1782,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -1928,7 +1929,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -2085,7 +2086,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( latestAgreementEntryPrimaryKey, @@ -2227,7 +2228,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( previousAgreementEntryPrimaryKey, diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index affd7e45fe..abe71bc8e4 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -47,6 +47,7 @@ import { writePlatformCatalogEntry, readAllTokenGenStatesItems, } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { dynamoDBClient } from "./utils.js"; @@ -139,7 +140,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -235,7 +236,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -351,7 +352,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -495,7 +496,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -680,7 +681,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -744,7 +745,7 @@ describe("integration tests V2 events", async () => { agreement.id ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -865,7 +866,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -1004,7 +1005,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -1072,7 +1073,7 @@ describe("integration tests V2 events", async () => { agreement.id ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -1193,7 +1194,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -1332,7 +1333,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -1400,7 +1401,7 @@ describe("integration tests V2 events", async () => { agreement.id ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -1521,7 +1522,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -1660,7 +1661,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -1728,7 +1729,7 @@ describe("integration tests V2 events", async () => { agreement.id ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -1849,7 +1850,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -1988,7 +1989,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -2057,7 +2058,7 @@ describe("integration tests V2 events", async () => { agreement.id ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -2178,7 +2179,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...previousAgreementStateEntry, @@ -2317,7 +2318,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { ...latestAgreementStateEntry, @@ -2445,7 +2446,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -2595,7 +2596,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( latestAgreementEntryPrimaryKey, @@ -2769,7 +2770,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( previousAgreementEntryPrimaryKey, @@ -2884,7 +2885,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedAgreementEntry = await readAgreementEntry( @@ -3027,7 +3028,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( agreementEntryPrimaryKey, @@ -3160,7 +3161,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( latestAgreementEntryPrimaryKey, @@ -3302,7 +3303,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( previousAgreementEntryPrimaryKey, diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index cdd9bb2e0c..9a52bb5520 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -38,6 +38,7 @@ import { it, vi, } from "vitest"; +import { genericLogger } from "pagopa-interop-commons"; import { z } from "zod"; import { updateAgreementStateInPlatformStatesEntry, @@ -508,6 +509,7 @@ describe("utils", async () => { dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger: genericLogger, }) ).resolves.not.toThrowError(); const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( @@ -589,6 +591,7 @@ describe("utils", async () => { dynamoDBClient, GSIPK_eserviceId_descriptorId, catalogEntry, + logger: genericLogger, }); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index d0317f231a..d3a53654e3 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -25,6 +25,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { Logger } from "pagopa-interop-commons"; import { clientKindToTokenGenerationStatesClientKind, convertEntriesToClientKidInTokenGenerationStates, @@ -48,7 +49,8 @@ import { export async function handleMessageV1( message: AuthorizationEventEnvelopeV1, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with({ type: "ClientAdded" }, async (msg) => { @@ -108,7 +110,8 @@ export async function handleMessageV1( const { purposeEntry, agreementEntry, catalogEntry } = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + logger ); const tokenClientKidPurposePK = @@ -184,7 +187,8 @@ export async function handleMessageV1( catalogEntry: catalogEntry2, } = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + logger ); const addedTokenGenStatesConsumerClient = addedEntries[index]; @@ -296,7 +300,11 @@ export async function handleMessageV1( return Promise.resolve(); } else { const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient, + logger + ); const seenKids = new Set(); const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = @@ -370,7 +378,8 @@ export async function handleMessageV1( catalogEntry: catalogEntry2, } = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + logger ); await updateTokenGenStatesDataForSecondRetrieval({ diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index f8f3447f1e..1df82f5d35 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -22,6 +22,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { match, P } from "ts-pattern"; +import { Logger } from "pagopa-interop-commons"; import { clientKindToTokenGenerationStatesClientKind, convertEntriesToClientKidInTokenGenerationStates, @@ -45,7 +46,8 @@ import { export async function handleMessageV2( message: AuthorizationEventEnvelopeV2, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) // eslint-disable-next-line sonarjs/cognitive-complexity @@ -90,7 +92,8 @@ export async function handleMessageV2( const { purposeEntry, agreementEntry, catalogEntry } = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + logger ); const tokenClientKidPurposePK = @@ -165,7 +168,8 @@ export async function handleMessageV2( catalogEntry: catalogEntry2, } = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + logger ); const addedTokenGenStatesConsumerClient = addedEntries[index]; @@ -278,7 +282,11 @@ export async function handleMessageV2( } else { const purposeId = unsafeBrandId(msg.data.purposeId); const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose(purposeId, dynamoDBClient); + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient, + logger + ); const seenKids = new Set(); const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = @@ -352,7 +360,8 @@ export async function handleMessageV2( catalogEntry: catalogEntry2, } = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + logger ); await updateTokenGenStatesDataForSecondRetrieval({ diff --git a/packages/authorization-platformstate-writer/src/index.ts b/packages/authorization-platformstate-writer/src/index.ts index 33da997245..adefd5382f 100644 --- a/packages/authorization-platformstate-writer/src/index.ts +++ b/packages/authorization-platformstate-writer/src/index.ts @@ -31,8 +31,12 @@ async function processMessage({ }); await match(decodedMessage) - .with({ event_version: 1 }, (msg) => handleMessageV1(msg, dynamoDBClient)) - .with({ event_version: 2 }, (msg) => handleMessageV2(msg, dynamoDBClient)) + .with({ event_version: 1 }, (msg) => + handleMessageV1(msg, dynamoDBClient, loggerInstance) + ) + .with({ event_version: 2 }, (msg) => + handleMessageV2(msg, dynamoDBClient, loggerInstance) + ) .exhaustive(); loggerInstance.info( diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index fb7ce4f438..d9f1faea66 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -52,6 +52,7 @@ import { TokenGenStatesGenericClientGSIKid, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { Logger } from "pagopa-interop-commons"; import { z } from "zod"; import { config } from "./config/config.js"; @@ -906,7 +907,8 @@ export const extractAgreementIdFromAgreementPK = ( export const retrievePlatformStatesByPurpose = async ( purposeId: PurposeId, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise<{ purposeEntry?: PlatformStatesPurposeEntry; agreementEntry?: PlatformStatesAgreementGSIAgreement; @@ -946,6 +948,10 @@ export const retrievePlatformStatesByPurpose = async ( eserviceId: purposeEntry.purposeEserviceId, descriptorId: agreementEntry.agreementDescriptorId, }); + + logger.info( + `Retrieving platform-states catalog entry ${catalogPK} to add descriptor info in token-generation-states` + ); const catalogEntry = await readPlatformCatalogEntry( catalogPK, dynamoDBClient diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index d3c04a176f..2b32dd102f 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -65,6 +65,7 @@ import { TokenGenerationStatesConsumerClient, toKeyV1, } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { clientKindToTokenGenerationStatesClientKind, @@ -126,7 +127,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -160,7 +161,7 @@ describe("integration tests V1 events", async () => { await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -214,7 +215,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -292,7 +293,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -354,7 +355,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -566,7 +567,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -901,7 +902,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1052,7 +1053,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1158,7 +1159,7 @@ describe("integration tests V1 events", async () => { await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); await writeTokenGenStatesApiClient(tokenClientEntry2, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1251,7 +1252,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1326,7 +1327,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -1423,7 +1424,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -1505,7 +1506,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1568,7 +1569,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1643,7 +1644,7 @@ describe("integration tests V1 events", async () => { ); await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -1809,7 +1810,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2086,7 +2087,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2400,7 +2401,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2528,7 +2529,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -2584,7 +2585,7 @@ describe("integration tests V1 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -2693,7 +2694,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2800,7 +2801,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntries = await readAllPlatformStatesItems( diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index 60c2227998..aa81fe9139 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -62,6 +62,7 @@ import { TokenGenerationStatesApiClient, TokenGenerationStatesConsumerClient, } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { clientKindToTokenGenerationStatesClientKind, @@ -141,7 +142,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -338,7 +339,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -626,7 +627,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -957,7 +958,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1093,7 +1094,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1188,7 +1189,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1290,7 +1291,7 @@ describe("integration tests V2 events", async () => { await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); await writeTokenGenStatesApiClient(tokenClientEntry2, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1383,7 +1384,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1451,7 +1452,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const platformClientPK = makePlatformStatesClientPK(client.id); @@ -1554,7 +1555,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -1630,7 +1631,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -1696,7 +1697,7 @@ describe("integration tests V2 events", async () => { ); await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -1855,7 +1856,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2124,7 +2125,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2431,7 +2432,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2559,7 +2560,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -2616,7 +2617,7 @@ describe("integration tests V2 events", async () => { }; await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformClientEntry = await readPlatformClientEntry( @@ -2725,7 +2726,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntry = await readPlatformClientEntry( @@ -2833,7 +2834,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformStatesEntries = await readAllPlatformStatesItems( diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 6b8a1f1ac2..15c9c51571 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -69,6 +69,7 @@ import { vi, } from "vitest"; import { z } from "zod"; +import { genericLogger } from "pagopa-interop-commons"; import { setClientPurposeIdsInPlatformStatesEntry, convertEntriesToClientKidInTokenGenerationStates, @@ -611,7 +612,8 @@ describe("utils", () => { const res = await retrievePlatformStatesByPurpose( purposeId, - dynamoDBClient + dynamoDBClient, + genericLogger ); expect(res).toEqual({ diff --git a/packages/authorization-server/src/model/domain/errors.ts b/packages/authorization-server/src/model/domain/errors.ts index e646fc024c..77aca0086a 100644 --- a/packages/authorization-server/src/model/domain/errors.ts +++ b/packages/authorization-server/src/model/domain/errors.ts @@ -22,30 +22,33 @@ export type ErrorCodes = keyof typeof errorCodes; export const makeApiProblem = makeApiProblemBuilder(errorCodes); export function clientAssertionRequestValidationFailed( - clientId: string | undefined + clientId: string | undefined, + details: string ): ApiError { return new ApiError({ - detail: `Client assertion request validation failed for request by client ${clientId}`, + detail: `Client assertion request validation failed for request by client ${clientId} - ${details}`, code: "clientAssertionRequestValidationFailed", title: "Client assertion request validation failed", }); } export function clientAssertionValidationFailed( - clientId: string | undefined + clientId: string | undefined, + details: string ): ApiError { return new ApiError({ - detail: `Client assertion validation failed for clientId: ${clientId}`, + detail: `Client assertion validation failed for clientId: ${clientId} - ${details}`, code: "clientAssertionValidationFailed", title: "Client assertion validation failed", }); } export function clientAssertionSignatureValidationFailed( - clientId: string | undefined + clientId: string | undefined, + details: string ): ApiError { return new ApiError({ - detail: `Client assertion signature validation failed for client ${clientId}`, + detail: `Client assertion signature validation failed for client ${clientId} - ${details}`, code: "clientAssertionSignatureValidationFailed", title: "Client assertion signature validation failed", }); @@ -88,10 +91,10 @@ export function incompleteTokenGenerationStatesConsumerClient( } export function platformStateValidationFailed( - details: string[] + details: string ): ApiError { return new ApiError({ - detail: `Platform state validation failed - reasons: ${details}`, + detail: `Platform state validation failed - ${details}`, code: "platformStateValidationFailed", title: "Platform state validation failed", }); diff --git a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts index 1504278003..4b8d4b79b9 100644 --- a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts +++ b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts @@ -1,3 +1,4 @@ +import { constants } from "http2"; import { ExpressContext, fromAppContext, @@ -8,7 +9,7 @@ import { ZodiosContext, zodiosValidationErrorToApiProblem, } from "pagopa-interop-commons"; -import { tooManyRequestsError } from "pagopa-interop-models"; +import { Problem, tooManyRequestsError } from "pagopa-interop-models"; import { authorizationServerApi } from "pagopa-interop-api-clients"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { initProducer } from "kafka-iam-auth"; @@ -95,7 +96,39 @@ const authorizationServerRouter = ( ctx.logger, ctx.correlationId ); - return res.status(errorRes.status).send(errorRes); + if (errorRes.status === constants.HTTP_STATUS_BAD_REQUEST) { + const cleanedError: Problem = { + title: "The request contains bad syntax or cannot be fulfilled.", + type: "about:blank", + status: constants.HTTP_STATUS_BAD_REQUEST, + detail: "Bad request", + errors: [ + { + code: "15-0008", + detail: "Unable to generate a token for the given request", + }, + ], + correlationId: ctx.correlationId, + }; + + return res.status(cleanedError.status).send(cleanedError); + } else { + const cleanedError: Problem = { + title: "The request couldn't be fulfilled due to an internal error", + type: "internalServerError", + status: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, + detail: "Internal server error", + errors: [ + { + code: "015-0000", + detail: + "Unable to generate a token for the given request due to an internal error", + }, + ], + correlationId: ctx.correlationId, + }; + return res.status(cleanedError.status).send(cleanedError); + } } }); return authorizationServerRouter; diff --git a/packages/authorization-server/src/services/tokenService.ts b/packages/authorization-server/src/services/tokenService.ts index 88a25b18fa..46241acbfa 100644 --- a/packages/authorization-server/src/services/tokenService.ts +++ b/packages/authorization-server/src/services/tokenService.ts @@ -24,6 +24,7 @@ import { ClientAssertion, FullTokenGenerationStatesConsumerClient, CorrelationId, + ClientKindTokenGenStates, } from "pagopa-interop-models"; import { DynamoDBClient, @@ -91,6 +92,8 @@ export function tokenServiceBuilder({ correlationId: CorrelationId, logger: Logger ): Promise { + logger.info(`CLIENTID=${request.client_id} Token requested`); + const { errors: parametersErrors } = validateRequestParameters({ client_assertion: request.client_assertion, client_assertion_type: request.client_assertion_type, @@ -99,7 +102,10 @@ export function tokenServiceBuilder({ }); if (parametersErrors) { - throw clientAssertionRequestValidationFailed(request.client_id); + throw clientAssertionRequestValidationFailed( + request.client_id, + parametersErrors.map((error) => error.detail).join(", ") + ); } const { data: jwt, errors: clientAssertionErrors } = @@ -110,15 +116,24 @@ export function tokenServiceBuilder({ ); if (clientAssertionErrors) { - // TODO double check if errors have to be logged or put inside the error below (check the same for parameters errors) - logger.warn(clientAssertionErrors.map((error) => error.detail)); - throw clientAssertionValidationFailed(request.client_id); + throw clientAssertionValidationFailed( + request.client_id, + clientAssertionErrors.map((error) => error.detail).join(", ") + ); } const clientId = jwt.payload.sub; const kid = jwt.header.kid; const purposeId = jwt.payload.purposeId; + logTokenGenerationInfo({ + validatedJwt: jwt, + clientKind: undefined, + tokenJti: undefined, + message: "Client assertion validated", + logger, + }); + const pk = purposeId ? makeTokenGenerationStatesClientKidPurposePK({ clientId, @@ -129,6 +144,14 @@ export function tokenServiceBuilder({ const key = await retrieveKey(dynamoDBClient, pk); + logTokenGenerationInfo({ + validatedJwt: jwt, + clientKind: key.clientKind, + tokenJti: undefined, + message: "Key retrieved", + logger, + }); + const { errors: clientAssertionSignatureErrors } = await verifyClientAssertionSignature( request.client_assertion, @@ -137,14 +160,17 @@ export function tokenServiceBuilder({ ); if (clientAssertionSignatureErrors) { - throw clientAssertionSignatureValidationFailed(request.client_id); + throw clientAssertionSignatureValidationFailed( + request.client_id, + clientAssertionSignatureErrors.map((error) => error.detail).join(", ") + ); } const { errors: platformStateErrors } = validateClientKindAndPlatformState(key, jwt); if (platformStateErrors) { throw platformStateValidationFailed( - platformStateErrors.map((error) => error.detail) + platformStateErrors.map((error) => error.detail).join(", ") ); } @@ -181,6 +207,14 @@ export function tokenServiceBuilder({ logger, }); + logTokenGenerationInfo({ + validatedJwt: jwt, + clientKind: key.clientKind, + tokenJti: token.payload.jti, + message: "Token generated", + logger, + }); + return { limitReached: false as const, token, @@ -194,6 +228,14 @@ export function tokenServiceBuilder({ consumerId: key.consumerId, }); + logTokenGenerationInfo({ + validatedJwt: jwt, + clientKind: key.clientKind, + tokenJti: token.payload.jti, + message: "Token generated", + logger, + }); + return { limitReached: false as const, token, @@ -375,3 +417,24 @@ const deconstructGSIPK_eserviceId_descriptorId = ( descriptorId: parsedDescriptorId.data, }; }; + +export const logTokenGenerationInfo = ({ + validatedJwt, + clientKind, + tokenJti, + message, + logger, +}: { + validatedJwt: ClientAssertion; + clientKind: ClientKindTokenGenStates | undefined; + tokenJti: string | undefined; + message: string; + logger: Logger; +}): void => { + const clientId = `[CLIENTID=${validatedJwt.payload.sub}`; + const kid = `[KID=${validatedJwt.header.kid}]`; + const purposeId = `[PURPOSEID=${validatedJwt.payload.purposeId}]`; + const tokenType = `[TYPE=${clientKind}]`; + const jti = `[JTI=${tokenJti}]`; + logger.info(`${clientId}${kid}${purposeId}${tokenType}${jti} - ${message}`); +}; diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts index d545f6e0d4..e2b2b7b5eb 100644 --- a/packages/authorization-server/test/authorizationServer.integration.test.ts +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -38,6 +38,12 @@ import { secondsToMilliseconds, } from "pagopa-interop-commons"; import { authorizationServerApi } from "pagopa-interop-api-clients"; +import { + invalidEServiceState, + invalidAssertionType, + invalidSignature, + issuedAtNotFound, +} from "pagopa-interop-client-assertion-validation"; import { config } from "../src/config/config.js"; import { clientAssertionRequestValidationFailed, @@ -85,15 +91,21 @@ describe("authorization server tests", () => { }; expect( tokenService.generateToken(request, generateId(), genericLogger) - ).rejects.toThrowError(clientAssertionRequestValidationFailed(clientId)); + ).rejects.toThrowError( + clientAssertionRequestValidationFailed( + clientId, + invalidAssertionType("wrong-client-assertion-type").detail + ) + ); }); it("should throw clientAssertionValidationFailed", async () => { + const clientId = generateId(); + const { jws } = await getMockClientAssertion({ - standardClaimsOverride: { iat: undefined }, + standardClaimsOverride: { iat: undefined, sub: clientId }, }); - const clientId = generateId(); const request: authorizationServerApi.AccessTokenRequest = { ...(await getMockAccessTokenRequest()), client_assertion: jws, @@ -101,7 +113,9 @@ describe("authorization server tests", () => { }; expect( tokenService.generateToken(request, generateId(), genericLogger) - ).rejects.toThrowError(clientAssertionValidationFailed(clientId)); + ).rejects.toThrowError( + clientAssertionValidationFailed(clientId, issuedAtNotFound().detail) + ); }); it("should throw tokenGenerationStatesEntryNotFound", async () => { @@ -209,10 +223,11 @@ describe("authorization server tests", () => { const purposeId = generateId(); const clientId = generateId(); - const { jws, clientAssertion } = await getMockClientAssertion({ - standardClaimsOverride: { sub: clientId }, - customClaims: { purposeId }, - }); + const { jws, clientAssertion, publicKeyEncodedPem } = + await getMockClientAssertion({ + standardClaimsOverride: { sub: clientId }, + customClaims: { purposeId }, + }); const splitJws = jws.split("."); const jwsWithWrongSignature = `${splitJws[0]}.${splitJws[1]}.wrong-singature`; @@ -231,8 +246,10 @@ describe("authorization server tests", () => { } ); - const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = - getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK); + const tokenClientKidPurposeEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), + publicKey: publicKeyEncodedPem, + }; await writeTokenGenStatesConsumerClient( tokenClientKidPurposeEntry, @@ -242,7 +259,10 @@ describe("authorization server tests", () => { expect( tokenService.generateToken(request, generateId(), genericLogger) ).rejects.toThrowError( - clientAssertionSignatureValidationFailed(request.client_id) + clientAssertionSignatureValidationFailed( + request.client_id, + invalidSignature().detail + ) ); }); @@ -285,7 +305,9 @@ describe("authorization server tests", () => { expect( tokenService.generateToken(request, generateId(), genericLogger) ).rejects.toThrowError( - platformStateValidationFailed([`E-Service state is: ${descriptorState}`]) + platformStateValidationFailed( + invalidEServiceState(descriptorState).detail + ) ); }); diff --git a/packages/client-assertion-validation/src/index.ts b/packages/client-assertion-validation/src/index.ts index 34e9d4ac37..5f9b9b9fea 100644 --- a/packages/client-assertion-validation/src/index.ts +++ b/packages/client-assertion-validation/src/index.ts @@ -1,3 +1,4 @@ export * from "./validation.js"; export * from "./types.js"; export * from "./config.js"; +export * from "./errors.js"; diff --git a/packages/models/src/errors.ts b/packages/models/src/errors.ts index 0a3deaefcb..6c32a7b389 100644 --- a/packages/models/src/errors.ts +++ b/packages/models/src/errors.ts @@ -54,7 +54,7 @@ type ProblemError = { detail: string; }; -type Problem = { +export type Problem = { type: string; status: number; title: string; diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV1.ts b/packages/purpose-platformstate-writer/src/consumerServiceV1.ts index 8e5b6e5b65..3cdd7ed0b8 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV1.ts @@ -9,6 +9,7 @@ import { PurposeV1, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { Logger } from "pagopa-interop-commons"; import { deletePlatformPurposeEntry, getPurposeStateFromPurposeVersions, @@ -23,7 +24,8 @@ import { export async function handleMessageV1( message: PurposeEventEnvelopeV1, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with({ type: "PurposeVersionActivated" }, async (msg) => { @@ -78,7 +80,8 @@ export async function handleMessageV1( dynamoDBClient, purpose, purposeState, - purposeVersion.id + purposeVersion.id, + logger ); } }) diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts index c884b9b894..c9def4df5b 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts @@ -13,6 +13,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { Logger } from "pagopa-interop-commons"; import { deletePlatformPurposeEntry, getPurposeStateFromPurposeVersions, @@ -26,7 +27,8 @@ import { export async function handleMessageV2( message: PurposeEventEnvelopeV2, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with( @@ -77,7 +79,8 @@ export async function handleMessageV2( dynamoDBClient, purpose, purposeState, - purposeVersion.id + purposeVersion.id, + logger ); } ) diff --git a/packages/purpose-platformstate-writer/src/index.ts b/packages/purpose-platformstate-writer/src/index.ts index 7d2bba5f79..108eac057c 100644 --- a/packages/purpose-platformstate-writer/src/index.ts +++ b/packages/purpose-platformstate-writer/src/index.ts @@ -31,8 +31,12 @@ async function processMessage({ }); await match(decodedMessage) - .with({ event_version: 1 }, (msg) => handleMessageV1(msg, dynamoDBClient)) - .with({ event_version: 2 }, (msg) => handleMessageV2(msg, dynamoDBClient)) + .with({ event_version: 1 }, (msg) => + handleMessageV1(msg, dynamoDBClient, loggerInstance) + ) + .with({ event_version: 2 }, (msg) => + handleMessageV2(msg, dynamoDBClient, loggerInstance) + ) .exhaustive(); loggerInstance.info( diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index 62d9964e95..a411d3439c 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -38,6 +38,7 @@ import { TokenGenStatesConsumerClientGSIPurpose, } from "pagopa-interop-models"; import { z } from "zod"; +import { Logger } from "pagopa-interop-commons"; import { config } from "./config/config.js"; export const getPurposeStateFromPurposeVersions = ( @@ -228,7 +229,8 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = dynamoDBClient: DynamoDBClient, purpose: Purpose, purposeState: ItemState, - purposeVersionId: PurposeVersionId + purposeVersionId: PurposeVersionId, + logger: Logger ): Promise => { const runPaginatedUpdateQuery = async ( dynamoDBClient: DynamoDBClient, @@ -236,6 +238,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = purposeState: ItemState, purposeVersionId: PurposeVersionId, exclusiveStartKey?: Record + // eslint-disable-next-line sonarjs/cognitive-complexity ): Promise => { const result = await readTokenGenStatesEntriesByGSIPKPurposeId( dynamoDBClient, @@ -246,28 +249,53 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); + const platformAgreementEntry = await readPlatformAgreementEntry( dynamoDBClient, gsiPKConsumerIdEServiceId ); - const catalogEntry = platformAgreementEntry - ? await readCatalogEntry( - dynamoDBClient, - makePlatformStatesEServiceDescriptorPK({ - eserviceId: purpose.eserviceId, - descriptorId: platformAgreementEntry.agreementDescriptorId, - }) - ) + + const { catalogEntryPK, gsiPKEServiceIdDescriptorId } = + platformAgreementEntry + ? { + catalogEntryPK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: purpose.eserviceId, + descriptorId: platformAgreementEntry.agreementDescriptorId, + }), + gsiPKEServiceIdDescriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: purpose.eserviceId, + descriptorId: platformAgreementEntry.agreementDescriptorId, + }), + } + : { + catalogEntryPK: undefined, + gsiPKEServiceIdDescriptorId: undefined, + }; + + if (catalogEntryPK) { + logger.info( + `Retrieving platform-states catalog entry ${catalogEntryPK} to add descriptor info in token-generation-states` + ); + } + + const catalogEntry = catalogEntryPK + ? await readCatalogEntry(dynamoDBClient, catalogEntryPK) : undefined; for (const entry of result.tokenGenStatesEntries) { const tokenEntryPK = entry.PK; const isAgreementMissingInTokenGenStates = - platformAgreementEntry && + !!platformAgreementEntry && + !!gsiPKEServiceIdDescriptorId && (!entry.agreementId || !entry.agreementState || !entry.GSIPK_eserviceId_descriptorId); + if (isAgreementMissingInTokenGenStates) { + logger.info( + `Adding agreement info to token-generation-states entry with PK ${tokenEntryPK} and GSIPK_consumerId_eserviceId ${gsiPKConsumerIdEServiceId}` + ); + } // Agreement data from platform-states const agreementExpressionAttributeValues: Record< string, @@ -281,10 +309,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = S: platformAgreementEntry.state, }, ":gsiPKEServiceIdDescriptorId": { - S: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose.eserviceId, - descriptorId: platformAgreementEntry.agreementDescriptorId, - }), + S: gsiPKEServiceIdDescriptorId, }, } : {}; @@ -296,12 +321,18 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = // Descriptor data from platform-states const isDescriptorDataMissingInTokenGenStates = - platformAgreementEntry && - catalogEntry && + !!platformAgreementEntry && + !!catalogEntry && (!entry.descriptorAudience || !entry.descriptorState || !entry.descriptorVoucherLifespan); + if (isDescriptorDataMissingInTokenGenStates) { + logger.info( + `Adding descriptor info to token-generation-states entry with PK ${tokenEntryPK} and GSIPK_eserviceId_descriptorId ${gsiPKEServiceIdDescriptorId}` + ); + } + const descriptorExpressionAttributeValues: Record< string, AttributeValue diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts index 238fceb4f0..c6f021c7c3 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -43,6 +43,7 @@ import { getMockTokenGenStatesConsumerClient, toPurposeV1, } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { getPurposeStateFromPurposeVersions, @@ -101,7 +102,7 @@ describe("integration tests for events V1", () => { // token-generation-states expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -192,7 +193,7 @@ describe("integration tests for events V1", () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -308,7 +309,7 @@ describe("integration tests for events V1", () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -424,7 +425,7 @@ describe("integration tests for events V1", () => { log_date: new Date(), }; - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -553,7 +554,7 @@ describe("integration tests for events V1", () => { log_date: new Date(), }; - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -706,7 +707,7 @@ describe("integration tests for events V1", () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient @@ -833,7 +834,7 @@ describe("integration tests for events V1", () => { log_date: new Date(), }; - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -936,7 +937,7 @@ describe("integration tests for events V1", () => { log_date: new Date(), }; - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1061,7 +1062,8 @@ describe("integration tests for events V1", () => { }; expect( - async () => await handleMessageV1(message, dynamoDBClient) + async () => + await handleMessageV1(message, dynamoDBClient, genericLogger) ).not.toThrowError(); // platform-states @@ -1175,7 +1177,7 @@ describe("integration tests for events V1", () => { log_date: new Date(), }; - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts index 4e6a714918..1d978a07d6 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -45,6 +45,7 @@ import { TokenGenerationStatesConsumerClient, toPurposeV2, } from "pagopa-interop-models"; +import { genericLogger } from "pagopa-interop-commons"; import { getMockTokenGenStatesConsumerClient } from "pagopa-interop-commons-test"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { @@ -104,7 +105,7 @@ describe("integration tests for events V2", () => { // token-generation-states expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -197,7 +198,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -310,7 +311,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -401,7 +402,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -562,7 +563,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient @@ -693,7 +694,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -804,7 +805,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -884,7 +885,7 @@ describe("integration tests for events V2", () => { // token-generation-states expect(await readAllTokenGenStatesItems(dynamoDBClient)).toHaveLength(0); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -978,7 +979,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1092,7 +1093,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1184,7 +1185,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1346,7 +1347,7 @@ describe("integration tests for events V2", () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient @@ -1477,7 +1478,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1582,7 +1583,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1711,7 +1712,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1836,7 +1837,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -1941,7 +1942,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2070,7 +2071,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2197,7 +2198,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2304,7 +2305,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2434,7 +2435,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2561,7 +2562,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2668,7 +2669,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2798,7 +2799,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -2934,7 +2935,7 @@ describe("integration tests for events V2", () => { log_date: new Date(), }; - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( diff --git a/packages/purpose-platformstate-writer/test/utils.test.ts b/packages/purpose-platformstate-writer/test/utils.test.ts index 491d147702..1356272a0a 100644 --- a/packages/purpose-platformstate-writer/test/utils.test.ts +++ b/packages/purpose-platformstate-writer/test/utils.test.ts @@ -43,6 +43,7 @@ import { getMockDescriptor, getMockAgreement, } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { deletePlatformPurposeEntry, getPurposeStateFromPurposeVersions, @@ -607,7 +608,8 @@ describe("utils tests", async () => { dynamoDBClient, purpose, itemState.inactive, - purpose.versions[0].id + purpose.versions[0].id, + genericLogger ) ).resolves.not.toThrowError(); const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( @@ -678,7 +680,8 @@ describe("utils tests", async () => { dynamoDBClient, purpose, itemState.active, - newPurposeVersionId + newPurposeVersionId, + genericLogger ); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ @@ -789,7 +792,8 @@ describe("utils tests", async () => { dynamoDBClient, purpose, itemState.active, - newPurposeVersionId + newPurposeVersionId, + genericLogger ); const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ @@ -928,7 +932,8 @@ describe("utils tests", async () => { dynamoDBClient, purpose, itemState.active, - newPurposeVersionId + newPurposeVersionId, + genericLogger ); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( From 03dedf553476cce5f8f98df921cc3df85b9971a6 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 12 Dec 2024 08:55:54 +0100 Subject: [PATCH 078/126] Fix error code in authorization server (#1285) --- .../src/routers/AuthorizationServerRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts index 4b8d4b79b9..044a2df8a6 100644 --- a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts +++ b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts @@ -104,7 +104,7 @@ const authorizationServerRouter = ( detail: "Bad request", errors: [ { - code: "15-0008", + code: "015-0008", detail: "Unable to generate a token for the given request", }, ], From 0afb5ba5e0c4181a37d7c925593c129b1b7a3bff Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 12 Dec 2024 10:01:25 +0100 Subject: [PATCH 079/126] PIN-5796 - Added missing e-service handling in enhance delegation bff function (#1281) --- packages/api-clients/open-api/bffApi.yml | 2 - .../src/api/delegationApiConverter.ts | 8 +- .../src/services/delegationService.ts | 78 +++++++++++++------ 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 325dd7de4e..9114bf29a2 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -15842,7 +15842,6 @@ components: $ref: "#/components/schemas/DelegationKind" required: - id - - eservice - delegate - delegator - state @@ -15866,7 +15865,6 @@ components: $ref: "#/components/schemas/DelegationKind" required: - id - - eserviceName - delegate - delegator - state diff --git a/packages/backend-for-frontend/src/api/delegationApiConverter.ts b/packages/backend-for-frontend/src/api/delegationApiConverter.ts index 95449a0521..58d97b0689 100644 --- a/packages/backend-for-frontend/src/api/delegationApiConverter.ts +++ b/packages/backend-for-frontend/src/api/delegationApiConverter.ts @@ -70,12 +70,12 @@ export function toBffDelegationApiDelegation( delegation: delegationApi.Delegation, delegator: tenantApi.Tenant, delegate: tenantApi.Tenant, - eservice: catalogApi.EService, + eservice: catalogApi.EService | undefined, producer: tenantApi.Tenant ): bffApi.Delegation { return { id: delegation.id, - eservice: { + eservice: eservice && { id: eservice.id, name: eservice.name, description: eservice.description, @@ -108,11 +108,11 @@ export function toBffDelegationApiCompactDelegation( delegation: delegationApi.Delegation, delegator: tenantApi.Tenant, delegate: tenantApi.Tenant, - eservice: catalogApi.EService + eservice: catalogApi.EService | undefined ): bffApi.CompactDelegation { return { id: delegation.id, - eservice: toCompactEserviceLight(eservice), + eservice: eservice && toCompactEserviceLight(eservice), delegate: { name: delegate.name, id: delegate.id, diff --git a/packages/backend-for-frontend/src/services/delegationService.ts b/packages/backend-for-frontend/src/services/delegationService.ts index f682224c60..a5d926d3b9 100644 --- a/packages/backend-for-frontend/src/services/delegationService.ts +++ b/packages/backend-for-frontend/src/services/delegationService.ts @@ -10,16 +10,13 @@ import { getAllFromPaginated, WithLogger, } from "pagopa-interop-commons"; -import { - DelegationContractId, - DelegationId, - delegationKind, -} from "pagopa-interop-models"; +import { DelegationContractId, DelegationId } from "pagopa-interop-models"; +import { isAxiosError } from "axios"; +import { match } from "ts-pattern"; import { DelegationsQueryParams, toBffDelegationApiCompactDelegation, toBffDelegationApiDelegation, - toDelegationKind, } from "../api/delegationApiConverter.js"; import { CatalogProcessClient, @@ -42,7 +39,7 @@ async function enhanceDelegation< delegation: delegationApi.Delegation, delegator: tenantApi.Tenant, delegate: tenantApi.Tenant, - eservice: catalogApi.EService, + eservice: catalogApi.EService | undefined, producer: tenantApi.Tenant ) => T, cachedTenants: Map = new Map() @@ -61,24 +58,59 @@ async function enhanceDelegation< cachedTenants ); - const eservice: catalogApi.EService = await catalogClient.getEServiceById({ - params: { eServiceId: delegation.eserviceId }, - headers, - }); - - // NOTE: If the delegation kind is DELEGATED_PRODUCER, the producer is the same as the delegator tenant. - // In the case of DELEGATED_CONSUMER, the producer can be different. - const producer = - delegation.kind === toDelegationKind(delegationKind.delegatedProducer) - ? await getTenantById( - tenantClient, + return await match(delegation.kind) + /** + * NOTE: + * If the delegation kind is DELEGATED_PRODUCER, the producer is the same as the delegator tenant. + * Plus the eservice might not exist anymore, since the delegator can delegate a deletable eservice, + * then revoke the delegation, and delete the e-service. + */ + .with(bffApi.DelegationKind.Values.DELEGATED_PRODUCER, async () => { + const eservice: catalogApi.EService | undefined = await (async () => { + try { + return await catalogClient.getEServiceById({ + params: { eServiceId: delegation.eserviceId }, + headers, + }); + } catch (err) { + if (isAxiosError(err) && err.response?.status === 404) { + return undefined; + } + throw err; + } + })(); + return toApiConverter( + delegation, + delegator, + delegate, + eservice, + delegator + ); + }) + .with(bffApi.DelegationKind.Values.DELEGATED_CONSUMER, async () => { + const eservice: catalogApi.EService = await catalogClient.getEServiceById( + { + params: { eServiceId: delegation.eserviceId }, headers, - eservice.producerId, - cachedTenants - ) - : delegator; + } + ); + + const producer = await getTenantById( + tenantClient, + headers, + eservice.producerId, + cachedTenants + ); - return toApiConverter(delegation, delegator, delegate, eservice, producer); + return toApiConverter( + delegation, + delegator, + delegate, + eservice, + producer + ); + }) + .exhaustive(); } export async function getDelegation( From 334665e310832f1cd6f68dd155f4d46842f168c3 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 12 Dec 2024 10:15:37 +0100 Subject: [PATCH 080/126] Updated delegator eservice event names (#1279) --- .../agreement-outbound-writer/package.json | 2 +- packages/authorization-updater/src/index.ts | 6 +- packages/catalog-outbound-writer/package.json | 2 +- .../src/converters/toOutboundEventV2.ts | 6 +- .../src/consumerServiceV2.ts | 6 +- .../src/model/domain/toEvent.ts | 12 +- .../src/services/catalogService.ts | 12 +- ...approveDelegatedEServiceDescriptor.test.ts | 14 +- .../test/publishDescriptor.test.ts | 6 +- .../rejectDelegatedEServiceDescriptor.test.ts | 6 +- .../src/consumerServiceV2.ts | 6 +- .../src/interfaceExporterV2.ts | 6 +- .../models/proto/v2/eservice/events.proto | 18 +- .../models/src/eservice/eserviceEvents.ts | 38 +- .../catalogItemEventNotificationConverter.ts | 6 +- .../catalogItemEventNotificationMessage.ts | 6 +- packages/purpose-outbound-writer/package.json | 2 +- packages/tenant-outbound-writer/package.json | 2 +- pnpm-lock.yaml | 716 ++++++++++++------ 19 files changed, 573 insertions(+), 299 deletions(-) diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index ec1964167f..ff778c2c94 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11d", + "@pagopa/interop-outbound-models": "1.0.11e", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/authorization-updater/src/index.ts b/packages/authorization-updater/src/index.ts index 5c82d0c844..fccf0ed1dc 100644 --- a/packages/authorization-updater/src/index.ts +++ b/packages/authorization-updater/src/index.ts @@ -68,7 +68,7 @@ export async function sendCatalogAuthUpdate( type: P.union( "EServiceDescriptorPublished", "EServiceDescriptorActivated", - "EServiceDescriptorDelegatorApproved" + "EServiceDescriptorApprovedByDelegator" ), }, async (msg) => { @@ -137,8 +137,8 @@ export async function sendCatalogAuthUpdate( "EServiceRiskAnalysisDeleted", "EServiceDescriptorAttributesUpdated", "EServiceDescriptionUpdated", - "EServiceDescriptorDelegateSubmitted", - "EServiceDescriptorDelegatorRejected" + "EServiceDescriptorSubmittedByDelegate", + "EServiceDescriptorRejectedByDelegator" ), }, () => { diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 262c62a0c6..56b8fcd01a 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11d", + "@pagopa/interop-outbound-models": "1.0.11e", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts index 33838006b7..9d99682c56 100644 --- a/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts +++ b/packages/catalog-outbound-writer/src/converters/toOutboundEventV2.ts @@ -98,9 +98,9 @@ export function toOutboundEventV2( { type: "EServiceDescriptorPublished" }, { type: "EServiceDescriptorSuspended" }, { type: "EServiceDraftDescriptorDeleted" }, - { type: "EServiceDescriptorDelegateSubmitted" }, - { type: "EServiceDescriptorDelegatorApproved" }, - { type: "EServiceDescriptorDelegatorRejected" }, + { type: "EServiceDescriptorSubmittedByDelegate" }, + { type: "EServiceDescriptorApprovedByDelegator" }, + { type: "EServiceDescriptorRejectedByDelegator" }, (msg) => ({ event_version: msg.event_version, type: msg.type, diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index 2ec1a636bb..cf3a5b569d 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -33,7 +33,7 @@ export async function handleMessageV2( await match(message) .with( { type: "EServiceDescriptorPublished" }, - { type: "EServiceDescriptorDelegatorApproved" }, + { type: "EServiceDescriptorApprovedByDelegator" }, async (msg) => { const { eservice, descriptor } = parseEServiceAndDescriptor( msg.data.eservice, @@ -243,9 +243,9 @@ export async function handleMessageV2( { type: "EServiceRiskAnalysisUpdated" }, { type: "EServiceRiskAnalysisDeleted" }, { type: "EServiceDescriptionUpdated" }, + { type: "EServiceDescriptorRejectedByDelegator" }, + { type: "EServiceDescriptorSubmittedByDelegate" }, { type: "EServiceDescriptorAttributesUpdated" }, - { type: "EServiceDescriptorDelegatorRejected" }, - { type: "EServiceDescriptorDelegateSubmitted" }, () => Promise.resolve() ) .exhaustive(); diff --git a/packages/catalog-process/src/model/domain/toEvent.ts b/packages/catalog-process/src/model/domain/toEvent.ts index 2ccbeef663..eeeaff4ce0 100644 --- a/packages/catalog-process/src/model/domain/toEvent.ts +++ b/packages/catalog-process/src/model/domain/toEvent.ts @@ -481,7 +481,7 @@ export const toCreateEventEServiceDescriptionUpdated = ( correlationId, }); -export const toCreateEventEServiceDescriptorDelegateSubmitted = ( +export const toCreateEventEServiceDescriptorSubmittedByDelegate = ( version: number, descriptorId: DescriptorId, eservice: EService, @@ -490,7 +490,7 @@ export const toCreateEventEServiceDescriptorDelegateSubmitted = ( streamId: eservice.id, version, event: { - type: "EServiceDescriptorDelegateSubmitted", + type: "EServiceDescriptorSubmittedByDelegate", event_version: 2, data: { descriptorId, @@ -500,7 +500,7 @@ export const toCreateEventEServiceDescriptorDelegateSubmitted = ( correlationId, }); -export const toCreateEventEServiceDescriptorDelegatorApproved = ( +export const toCreateEventEServiceDescriptorApprovedByDelegator = ( version: number, descriptorId: DescriptorId, eservice: EService, @@ -509,7 +509,7 @@ export const toCreateEventEServiceDescriptorDelegatorApproved = ( streamId: eservice.id, version, event: { - type: "EServiceDescriptorDelegatorApproved", + type: "EServiceDescriptorApprovedByDelegator", event_version: 2, data: { descriptorId, @@ -519,7 +519,7 @@ export const toCreateEventEServiceDescriptorDelegatorApproved = ( correlationId, }); -export const toCreateEventEServiceDescriptorDelegatorRejected = ( +export const toCreateEventEServiceDescriptorRejectedByDelegator = ( version: number, descriptorId: DescriptorId, eservice: EService, @@ -528,7 +528,7 @@ export const toCreateEventEServiceDescriptorDelegatorRejected = ( streamId: eservice.id, version, event: { - type: "EServiceDescriptorDelegatorRejected", + type: "EServiceDescriptorRejectedByDelegator", event_version: 2, data: { descriptorId, diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 69706334b8..4d976a99e6 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -81,10 +81,10 @@ import { toCreateEventEServiceDescriptorActivated, toCreateEventEServiceDescriptorAdded, toCreateEventEServiceDescriptorArchived, + toCreateEventEServiceDescriptorSubmittedByDelegate, + toCreateEventEServiceDescriptorApprovedByDelegator, + toCreateEventEServiceDescriptorRejectedByDelegator, toCreateEventEServiceDescriptorAttributesUpdated, - toCreateEventEServiceDescriptorDelegateSubmitted, - toCreateEventEServiceDescriptorDelegatorApproved, - toCreateEventEServiceDescriptorDelegatorRejected, toCreateEventEServiceDescriptorPublished, toCreateEventEServiceDescriptorQuotasUpdated, toCreateEventEServiceDescriptorSuspended, @@ -1232,7 +1232,7 @@ export function catalogServiceBuilder( updateDescriptorState(descriptor, descriptorState.waitingForApproval) ); await repository.createEvent( - toCreateEventEServiceDescriptorDelegateSubmitted( + toCreateEventEServiceDescriptorSubmittedByDelegate( eservice.metadata.version, descriptor.id, eserviceWithWaitingForApprovalDescriptor, @@ -1831,7 +1831,7 @@ export function catalogServiceBuilder( ); await repository.createEvent( - toCreateEventEServiceDescriptorDelegatorApproved( + toCreateEventEServiceDescriptorApprovedByDelegator( eservice.metadata.version, descriptor.id, updatedEService, @@ -1881,7 +1881,7 @@ export function catalogServiceBuilder( ); await repository.createEvent( - toCreateEventEServiceDescriptorDelegatorRejected( + toCreateEventEServiceDescriptorRejectedByDelegator( eservice.metadata.version, descriptor.id, updatedEService, diff --git a/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts b/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts index ed06263ca1..a28be8233e 100644 --- a/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts +++ b/packages/catalog-process/test/approveDelegatedEServiceDescriptor.test.ts @@ -14,7 +14,7 @@ import { generateId, operationForbidden, delegationState, - EServiceDescriptorDelegatorApprovedV2, + EServiceDescriptorApprovedByDelegatorV2, delegationKind, } from "pagopa-interop-models"; import { beforeAll, vi, afterAll, expect, describe, it } from "vitest"; @@ -74,11 +74,11 @@ describe("publish descriptor", () => { expect(writtenEvent).toMatchObject({ stream_id: eservice.id, version: "1", - type: "EServiceDescriptorDelegatorApproved", + type: "EServiceDescriptorApprovedByDelegator", event_version: 2, }); const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorDelegatorApprovedV2, + messageType: EServiceDescriptorApprovedByDelegatorV2, payload: writtenEvent.data, }); @@ -131,12 +131,12 @@ describe("publish descriptor", () => { expect(writtenEvent).toMatchObject({ stream_id: eservice.id, version: "1", - type: "EServiceDescriptorDelegatorApproved", + type: "EServiceDescriptorApprovedByDelegator", event_version: 2, }); const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorDelegatorApprovedV2, + messageType: EServiceDescriptorApprovedByDelegatorV2, payload: writtenEvent.data, }); @@ -206,11 +206,11 @@ describe("publish descriptor", () => { expect(writtenEvent).toMatchObject({ stream_id: eservice.id, version: "1", - type: "EServiceDescriptorDelegatorApproved", + type: "EServiceDescriptorApprovedByDelegator", event_version: 2, }); const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorDelegatorApprovedV2, + messageType: EServiceDescriptorApprovedByDelegatorV2, payload: writtenEvent.data, }); diff --git a/packages/catalog-process/test/publishDescriptor.test.ts b/packages/catalog-process/test/publishDescriptor.test.ts index 729b9a1e07..562dca91f9 100644 --- a/packages/catalog-process/test/publishDescriptor.test.ts +++ b/packages/catalog-process/test/publishDescriptor.test.ts @@ -20,7 +20,7 @@ import { generateId, operationForbidden, delegationState, - EServiceDescriptorDelegateSubmittedV2, + EServiceDescriptorSubmittedByDelegateV2, delegationKind, } from "pagopa-interop-models"; import { beforeAll, vi, afterAll, expect, describe, it } from "vitest"; @@ -220,11 +220,11 @@ describe("publish descriptor", () => { expect(writtenEvent).toMatchObject({ stream_id: eservice.id, version: "1", - type: "EServiceDescriptorDelegateSubmitted", + type: "EServiceDescriptorSubmittedByDelegate", event_version: 2, }); const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorDelegateSubmittedV2, + messageType: EServiceDescriptorSubmittedByDelegateV2, payload: writtenEvent.data, }); diff --git a/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts b/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts index efabfe06e5..95172397c9 100644 --- a/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts +++ b/packages/catalog-process/test/rejectDelegatedEServiceDescriptor.test.ts @@ -12,7 +12,7 @@ import { operationForbidden, delegationState, DescriptorRejectionReason, - EServiceDescriptorDelegatorRejectedV2, + EServiceDescriptorRejectedByDelegatorV2, generateId, delegationKind, } from "pagopa-interop-models"; @@ -76,11 +76,11 @@ describe("reject descriptor", () => { expect(writtenEvent).toMatchObject({ stream_id: eservice.id, version: "1", - type: "EServiceDescriptorDelegatorRejected", + type: "EServiceDescriptorRejectedByDelegator", event_version: 2, }); const writtenPayload = decodeProtobufPayload({ - messageType: EServiceDescriptorDelegatorRejectedV2, + messageType: EServiceDescriptorRejectedByDelegatorV2, payload: writtenEvent.data, }); diff --git a/packages/catalog-readmodel-writer/src/consumerServiceV2.ts b/packages/catalog-readmodel-writer/src/consumerServiceV2.ts index 9f9a87b690..62591ebcae 100644 --- a/packages/catalog-readmodel-writer/src/consumerServiceV2.ts +++ b/packages/catalog-readmodel-writer/src/consumerServiceV2.ts @@ -41,10 +41,10 @@ export async function handleMessageV2( { type: "EServiceRiskAnalysisUpdated" }, { type: "EServiceRiskAnalysisDeleted" }, { type: "EServiceDescriptionUpdated" }, + { type: "EServiceDescriptorSubmittedByDelegate" }, + { type: "EServiceDescriptorApprovedByDelegator" }, + { type: "EServiceDescriptorRejectedByDelegator" }, { type: "EServiceDescriptorAttributesUpdated" }, - { type: "EServiceDescriptorDelegateSubmitted" }, - { type: "EServiceDescriptorDelegatorApproved" }, - { type: "EServiceDescriptorDelegatorRejected" }, async (message) => await eservices.updateOne( { diff --git a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts index 640dd3e179..33a45bb8e6 100644 --- a/packages/datalake-interface-exporter/src/interfaceExporterV2.ts +++ b/packages/datalake-interface-exporter/src/interfaceExporterV2.ts @@ -13,7 +13,7 @@ export async function exportInterfaceV2( await match(decodedMsg) .with( { type: "EServiceDescriptorPublished" }, - { type: "EServiceDescriptorDelegatorApproved" }, + { type: "EServiceDescriptorApprovedByDelegator" }, async ({ data }) => { if (data.eservice) { logger.info( @@ -56,8 +56,8 @@ export async function exportInterfaceV2( { type: "EServiceRiskAnalysisUpdated" }, { type: "EServiceRiskAnalysisDeleted" }, { type: "EServiceDescriptionUpdated" }, - { type: "EServiceDescriptorDelegateSubmitted" }, - { type: "EServiceDescriptorDelegatorRejected" }, + { type: "EServiceDescriptorSubmittedByDelegate" }, + { type: "EServiceDescriptorRejectedByDelegator" }, { type: "EServiceDescriptorAttributesUpdated" }, () => undefined ) diff --git a/packages/models/proto/v2/eservice/events.proto b/packages/models/proto/v2/eservice/events.proto index 711e2bad96..9fe2a962da 100644 --- a/packages/models/proto/v2/eservice/events.proto +++ b/packages/models/proto/v2/eservice/events.proto @@ -118,23 +118,23 @@ message EServiceDescriptionUpdatedV2 { EServiceV2 eservice = 1; } -message EServiceDescriptorAttributesUpdatedV2 { - string descriptorId = 1; - repeated string attributeIds = 2; - EServiceV2 eservice = 3; -} - -message EServiceDescriptorDelegateSubmittedV2 { +message EServiceDescriptorSubmittedByDelegateV2 { string descriptorId = 1; EServiceV2 eservice = 2; } -message EServiceDescriptorDelegatorApprovedV2 { +message EServiceDescriptorApprovedByDelegatorV2 { string descriptorId = 1; EServiceV2 eservice = 2; } -message EServiceDescriptorDelegatorRejectedV2 { +message EServiceDescriptorRejectedByDelegatorV2 { string descriptorId = 1; EServiceV2 eservice = 2; } + +message EServiceDescriptorAttributesUpdatedV2 { + string descriptorId = 1; + repeated string attributeIds = 2; + EServiceV2 eservice = 3; +} diff --git a/packages/models/src/eservice/eserviceEvents.ts b/packages/models/src/eservice/eserviceEvents.ts index 7687343f31..3c60456e52 100644 --- a/packages/models/src/eservice/eserviceEvents.ts +++ b/packages/models/src/eservice/eserviceEvents.ts @@ -41,10 +41,10 @@ import { EServiceRiskAnalysisUpdatedV2, EServiceRiskAnalysisDeletedV2, EServiceDescriptionUpdatedV2, + EServiceDescriptorSubmittedByDelegateV2, + EServiceDescriptorApprovedByDelegatorV2, + EServiceDescriptorRejectedByDelegatorV2, EServiceDescriptorAttributesUpdatedV2, - EServiceDescriptorDelegateSubmittedV2, - EServiceDescriptorDelegatorApprovedV2, - EServiceDescriptorDelegatorRejectedV2, } from "../gen/v2/eservice/events.js"; export function catalogEventToBinaryData(event: EServiceEvent): Uint8Array { @@ -169,17 +169,17 @@ export function catalogEventToBinaryDataV2(event: EServiceEventV2): Uint8Array { .with({ type: "EServiceDescriptionUpdated" }, ({ data }) => EServiceDescriptionUpdatedV2.toBinary(data) ) - .with({ type: "EServiceDescriptorAttributesUpdated" }, ({ data }) => - EServiceDescriptorAttributesUpdatedV2.toBinary(data) + .with({ type: "EServiceDescriptorSubmittedByDelegate" }, ({ data }) => + EServiceDescriptorSubmittedByDelegateV2.toBinary(data) ) - .with({ type: "EServiceDescriptorDelegateSubmitted" }, ({ data }) => - EServiceDescriptorDelegateSubmittedV2.toBinary(data) + .with({ type: "EServiceDescriptorApprovedByDelegator" }, ({ data }) => + EServiceDescriptorApprovedByDelegatorV2.toBinary(data) ) - .with({ type: "EServiceDescriptorDelegatorApproved" }, ({ data }) => - EServiceDescriptorDelegatorApprovedV2.toBinary(data) + .with({ type: "EServiceDescriptorRejectedByDelegator" }, ({ data }) => + EServiceDescriptorRejectedByDelegatorV2.toBinary(data) ) - .with({ type: "EServiceDescriptorDelegatorRejected" }, ({ data }) => - EServiceDescriptorDelegatorRejectedV2.toBinary(data) + .with({ type: "EServiceDescriptorAttributesUpdated" }, ({ data }) => + EServiceDescriptorAttributesUpdatedV2.toBinary(data) ) .exhaustive(); } @@ -371,23 +371,23 @@ export const EServiceEventV2 = z.discriminatedUnion("type", [ }), z.object({ event_version: z.literal(2), - type: z.literal("EServiceDescriptorAttributesUpdated"), - data: protobufDecoder(EServiceDescriptorAttributesUpdatedV2), + type: z.literal("EServiceDescriptorSubmittedByDelegate"), + data: protobufDecoder(EServiceDescriptorSubmittedByDelegateV2), }), z.object({ event_version: z.literal(2), - type: z.literal("EServiceDescriptorDelegateSubmitted"), - data: protobufDecoder(EServiceDescriptorDelegateSubmittedV2), + type: z.literal("EServiceDescriptorApprovedByDelegator"), + data: protobufDecoder(EServiceDescriptorApprovedByDelegatorV2), }), z.object({ event_version: z.literal(2), - type: z.literal("EServiceDescriptorDelegatorApproved"), - data: protobufDecoder(EServiceDescriptorDelegatorApprovedV2), + type: z.literal("EServiceDescriptorRejectedByDelegator"), + data: protobufDecoder(EServiceDescriptorRejectedByDelegatorV2), }), z.object({ event_version: z.literal(2), - type: z.literal("EServiceDescriptorDelegatorRejected"), - data: protobufDecoder(EServiceDescriptorDelegatorRejectedV2), + type: z.literal("EServiceDescriptorAttributesUpdated"), + data: protobufDecoder(EServiceDescriptorAttributesUpdatedV2), }), ]); export type EServiceEventV2 = z.infer; diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts index a39187874a..09d9fa2424 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationConverter.ts @@ -108,9 +108,9 @@ export const toCatalogItemEventNotification = ( { type: "EServiceDescriptorArchived" }, // CatalogItemDescriptorUpdatedV1 { type: "EServiceDescriptorPublished" }, // CatalogItemDescriptorUpdatedV1 { type: "EServiceDescriptorSuspended" }, // CatalogItemDescriptorUpdatedV1 - { type: "EServiceDescriptorDelegateSubmitted" }, - { type: "EServiceDescriptorDelegatorApproved" }, - { type: "EServiceDescriptorDelegatorRejected" }, + { type: "EServiceDescriptorSubmittedByDelegate" }, + { type: "EServiceDescriptorApprovedByDelegator" }, + { type: "EServiceDescriptorRejectedByDelegator" }, { type: "EServiceDescriptorQuotasUpdated" }, { type: "EServiceDescriptorAttributesUpdated" }, (e): CatalogDescriptorNotification => { diff --git a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts index bd166779ff..3d6f0354ee 100644 --- a/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts +++ b/packages/notifier-seeder/src/models/catalog/catalogItemEventNotificationMessage.ts @@ -31,10 +31,10 @@ export const eventV2TypeMapper = ( "EServiceDescriptorArchived", "EServiceDescriptorPublished", "EServiceDescriptorSuspended", + "EServiceDescriptorSubmittedByDelegate", + "EServiceDescriptorApprovedByDelegator", + "EServiceDescriptorRejectedByDelegator", "EServiceDescriptorAttributesUpdated", - "EServiceDescriptorDelegateSubmitted", - "EServiceDescriptorDelegatorApproved", - "EServiceDescriptorDelegatorRejected", () => "catalog_item_descriptor_updated" ) .with( diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index ef27a5a998..ac1de66964 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11d", + "@pagopa/interop-outbound-models": "1.0.11e", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 15ea0468e5..2ff794e76a 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11d", + "@pagopa/interop-outbound-models": "1.0.11e", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46cb0f632b..1069e32b6d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11d - version: 1.0.11-d + specifier: 1.0.11e + version: 1.0.11-e '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1063,8 +1063,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11d - version: 1.0.11-d + specifier: 1.0.11e + version: 1.0.11-e '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2595,8 +2595,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11d - version: 1.0.11-d + specifier: 1.0.11e + version: 1.0.11-e '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2900,8 +2900,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11d - version: 1.0.11-d + specifier: 1.0.11e + version: 1.0.11-e '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3423,6 +3423,10 @@ packages: resolution: {integrity: sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==} engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.667.0': + resolution: {integrity: sha512-gYq0xCsqFfQaSL/yT1Gl1vIUjtsg7d7RhnUfsXaHt8xTxOKRTdH9GjbesBjXOzgOvB0W0vfssfreSNGFlOOMJg==} + engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.692.0': resolution: {integrity: sha512-RpNvzD7zMEhiKgmlxGzyXaEcg2khvM7wd5sSHVapOcrde1awQSOMGI4zKBQ+wy5TnDfrm170ROz/ERLYtrjPZA==} engines: {node: '>=16.0.0'} @@ -3959,8 +3963,8 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.11-d': - resolution: {integrity: sha512-CAA22QItRcbxqSqJhKDvWCuHA+KjyzYeO0hYupJIinAcoQkao+MFnN+nxgI+z6GN23C7uhJIfX987LIra1YWdg==} + '@pagopa/interop-outbound-models@1.0.11-e': + resolution: {integrity: sha512-SB3GOaHYwTuGFw2Jr6mlFnekQNZuLy1E63UuIhMk0KXFKTTxOnBT6fdfyQj4WrAnVmzbGAvVQ/WmMz9I00n4UQ==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -4115,6 +4119,10 @@ packages: '@sinonjs/text-encoding@0.7.3': resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} + '@smithy/abort-controller@3.1.5': + resolution: {integrity: sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg==} + engines: {node: '>=16.0.0'} + '@smithy/abort-controller@3.1.8': resolution: {integrity: sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==} engines: {node: '>=16.0.0'} @@ -4129,12 +4137,20 @@ packages: resolution: {integrity: sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==} engines: {node: '>=16.0.0'} + '@smithy/config-resolver@3.0.9': + resolution: {integrity: sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg==} + engines: {node: '>=16.0.0'} + + '@smithy/core@2.4.8': + resolution: {integrity: sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA==} + engines: {node: '>=16.0.0'} + '@smithy/core@2.5.4': resolution: {integrity: sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==} engines: {node: '>=16.0.0'} - '@smithy/credential-provider-imds@3.1.3': - resolution: {integrity: sha512-U1Yrv6hx/mRK6k8AncuI6jLUx9rn0VVSd9NPEX6pyYFBfkSkChOc/n4zUb8alHUVg83TbI4OdZVo1X0Zfj3ijA==} + '@smithy/credential-provider-imds@3.2.4': + resolution: {integrity: sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w==} engines: {node: '>=16.0.0'} '@smithy/credential-provider-imds@3.2.7': @@ -4173,8 +4189,8 @@ packages: resolution: {integrity: sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==} engines: {node: '>=16.0.0'} - '@smithy/hash-node@3.0.3': - resolution: {integrity: sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==} + '@smithy/hash-node@3.0.7': + resolution: {integrity: sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw==} engines: {node: '>=16.0.0'} '@smithy/hash-stream-node@3.1.9': @@ -4184,8 +4200,8 @@ packages: '@smithy/invalid-dependency@3.0.10': resolution: {integrity: sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==} - '@smithy/invalid-dependency@3.0.3': - resolution: {integrity: sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==} + '@smithy/invalid-dependency@3.0.7': + resolution: {integrity: sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA==} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} @@ -4202,10 +4218,22 @@ packages: resolution: {integrity: sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==} engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@3.0.9': + resolution: {integrity: sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA==} + engines: {node: '>=16.0.0'} + + '@smithy/middleware-endpoint@3.1.4': + resolution: {integrity: sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@3.2.4': resolution: {integrity: sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==} engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.23': + resolution: {integrity: sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@3.0.28': resolution: {integrity: sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==} engines: {node: '>=16.0.0'} @@ -4214,14 +4242,30 @@ packages: resolution: {integrity: sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==} engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@3.0.7': + resolution: {integrity: sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g==} + engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.10': resolution: {integrity: sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==} engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@3.0.7': + resolution: {integrity: sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA==} + engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.11': resolution: {integrity: sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==} engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@3.1.8': + resolution: {integrity: sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q==} + engines: {node: '>=16.0.0'} + + '@smithy/node-http-handler@3.2.4': + resolution: {integrity: sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ==} + engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@3.3.1': resolution: {integrity: sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==} engines: {node: '>=16.0.0'} @@ -4230,8 +4274,12 @@ packages: resolution: {integrity: sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==} engines: {node: '>=16.0.0'} - '@smithy/property-provider@3.1.3': - resolution: {integrity: sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==} + '@smithy/property-provider@3.1.7': + resolution: {integrity: sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw==} + engines: {node: '>=16.0.0'} + + '@smithy/protocol-http@4.1.4': + resolution: {integrity: sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ==} engines: {node: '>=16.0.0'} '@smithy/protocol-http@4.1.7': @@ -4246,18 +4294,34 @@ packages: resolution: {integrity: sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==} engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@3.0.7': + resolution: {integrity: sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw==} + engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.10': resolution: {integrity: sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==} engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@3.0.7': + resolution: {integrity: sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA==} + engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.10': resolution: {integrity: sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==} engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@3.0.7': + resolution: {integrity: sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA==} + engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.11': resolution: {integrity: sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==} engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@3.1.8': + resolution: {integrity: sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw==} + engines: {node: '>=16.0.0'} + '@smithy/signature-v4@2.3.0': resolution: {integrity: sha512-ui/NlpILU+6HAQBfJX8BBsDXuKSNrjTSuOYArRblcrErwKFutjrCNb/OExfVRyj9+26F9J+ZmfWT+fKWuDrH3Q==} engines: {node: '>=14.0.0'} @@ -4270,6 +4334,10 @@ packages: resolution: {integrity: sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==} engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.4.0': + resolution: {integrity: sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ==} + engines: {node: '>=16.0.0'} + '@smithy/smithy-client@3.4.5': resolution: {integrity: sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==} engines: {node: '>=16.0.0'} @@ -4278,6 +4346,10 @@ packages: resolution: {integrity: sha512-QwYgloJ0sVNBeBuBs65cIkTbfzV/Q6ZNPCJ99EICFEdJYG50nGIY/uYXp+TbsdJReIuPr0a0kXmCvren3MbRRw==} engines: {node: '>=14.0.0'} + '@smithy/types@3.5.0': + resolution: {integrity: sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q==} + engines: {node: '>=16.0.0'} + '@smithy/types@3.7.1': resolution: {integrity: sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==} engines: {node: '>=16.0.0'} @@ -4285,6 +4357,9 @@ packages: '@smithy/url-parser@3.0.10': resolution: {integrity: sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==} + '@smithy/url-parser@3.0.7': + resolution: {integrity: sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA==} + '@smithy/util-base64@3.0.0': resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} engines: {node: '>=16.0.0'} @@ -4308,14 +4383,26 @@ packages: resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} engines: {node: '>=16.0.0'} + '@smithy/util-defaults-mode-browser@3.0.23': + resolution: {integrity: sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw==} + engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@3.0.28': resolution: {integrity: sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==} engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.23': + resolution: {integrity: sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg==} + engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@3.0.28': resolution: {integrity: sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==} engines: {node: '>= 10.0.0'} + '@smithy/util-endpoints@2.1.3': + resolution: {integrity: sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw==} + engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@2.1.6': resolution: {integrity: sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==} engines: {node: '>=16.0.0'} @@ -4336,10 +4423,22 @@ packages: resolution: {integrity: sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==} engines: {node: '>=16.0.0'} + '@smithy/util-middleware@3.0.7': + resolution: {integrity: sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA==} + engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.10': resolution: {integrity: sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==} engines: {node: '>=16.0.0'} + '@smithy/util-retry@3.0.7': + resolution: {integrity: sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug==} + engines: {node: '>=16.0.0'} + + '@smithy/util-stream@3.1.9': + resolution: {integrity: sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ==} + engines: {node: '>=16.0.0'} + '@smithy/util-stream@3.3.1': resolution: {integrity: sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==} engines: {node: '>=16.0.0'} @@ -5022,14 +5121,14 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - csv-generate@4.4.2: - resolution: {integrity: sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==} + csv-generate@4.4.1: + resolution: {integrity: sha512-O/einO0v4zPmXaOV+sYqGa02VkST4GP5GLpWBNHEouIU7pF3kpGf3D0kCCvX82ydIY4EKkOK+R8b1BYsRXravg==} - csv-parse@5.6.0: - resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + csv-parse@5.5.6: + resolution: {integrity: sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==} - csv-stringify@6.5.2: - resolution: {integrity: sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==} + csv-stringify@6.5.1: + resolution: {integrity: sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==} csv@6.3.2: resolution: {integrity: sha512-fOm1LBmt4/kjC1RFanNtjSFVjvoh6MS5E/CuQrED5gCfvjHESZD97Fbjfz/W8ZN4wQAxFjzOonATE790UIuLTg==} @@ -5411,6 +5510,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@3.0.0-alpha-1: @@ -6915,8 +7015,8 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - stream-transform@3.3.3: - resolution: {integrity: sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==} + stream-transform@3.3.2: + resolution: {integrity: sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ==} streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -7495,7 +7595,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.692.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-locate-window': 3.568.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7503,7 +7603,7 @@ snapshots: '@aws-crypto/sha256-js@4.0.0': dependencies: '@aws-crypto/util': 4.0.0 - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 tslib: 1.14.1 '@aws-crypto/sha256-js@5.2.0': @@ -7518,13 +7618,13 @@ snapshots: '@aws-crypto/util@4.0.0': dependencies: - '@aws-sdk/types': 3.609.0 + '@aws-sdk/types': 3.667.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.692.0 + '@aws-sdk/types': 3.667.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7545,30 +7645,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.12 - '@smithy/core': 2.5.4 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.12 - '@smithy/middleware-endpoint': 3.2.4 - '@smithy/middleware-retry': 3.0.28 - '@smithy/middleware-serde': 3.0.10 - '@smithy/middleware-stack': 3.0.10 - '@smithy/node-config-provider': 3.1.11 - '@smithy/node-http-handler': 3.3.1 - '@smithy/protocol-http': 4.1.7 - '@smithy/smithy-client': 3.4.5 - '@smithy/types': 3.7.1 - '@smithy/url-parser': 3.0.10 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.28 - '@smithy/util-defaults-mode-node': 3.0.28 - '@smithy/util-endpoints': 2.1.6 - '@smithy/util-middleware': 3.0.10 - '@smithy/util-retry': 3.0.10 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -7843,30 +7943,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.12 - '@smithy/core': 2.5.4 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.12 - '@smithy/middleware-endpoint': 3.2.4 - '@smithy/middleware-retry': 3.0.28 - '@smithy/middleware-serde': 3.0.10 - '@smithy/middleware-stack': 3.0.10 - '@smithy/node-config-provider': 3.1.11 - '@smithy/node-http-handler': 3.3.1 - '@smithy/protocol-http': 4.1.7 - '@smithy/smithy-client': 3.4.5 - '@smithy/types': 3.7.1 - '@smithy/url-parser': 3.0.10 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.28 - '@smithy/util-defaults-mode-node': 3.0.28 - '@smithy/util-endpoints': 2.1.6 - '@smithy/util-middleware': 3.0.10 - '@smithy/util-retry': 3.0.10 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -7931,30 +8031,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.12 - '@smithy/core': 2.5.4 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.12 - '@smithy/middleware-endpoint': 3.2.4 - '@smithy/middleware-retry': 3.0.28 - '@smithy/middleware-serde': 3.0.10 - '@smithy/middleware-stack': 3.0.10 - '@smithy/node-config-provider': 3.1.11 - '@smithy/node-http-handler': 3.3.1 - '@smithy/protocol-http': 4.1.7 - '@smithy/smithy-client': 3.4.5 - '@smithy/types': 3.7.1 - '@smithy/url-parser': 3.0.10 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.28 - '@smithy/util-defaults-mode-node': 3.0.28 - '@smithy/util-endpoints': 2.1.6 - '@smithy/util-middleware': 3.0.10 - '@smithy/util-retry': 3.0.10 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8019,30 +8119,30 @@ snapshots: '@aws-sdk/util-endpoints': 3.609.0 '@aws-sdk/util-user-agent-browser': 3.609.0 '@aws-sdk/util-user-agent-node': 3.609.0 - '@smithy/config-resolver': 3.0.12 - '@smithy/core': 2.5.4 + '@smithy/config-resolver': 3.0.9 + '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.3 - '@smithy/invalid-dependency': 3.0.3 - '@smithy/middleware-content-length': 3.0.12 - '@smithy/middleware-endpoint': 3.2.4 - '@smithy/middleware-retry': 3.0.28 - '@smithy/middleware-serde': 3.0.10 - '@smithy/middleware-stack': 3.0.10 - '@smithy/node-config-provider': 3.1.11 - '@smithy/node-http-handler': 3.3.1 - '@smithy/protocol-http': 4.1.7 - '@smithy/smithy-client': 3.4.5 - '@smithy/types': 3.7.1 - '@smithy/url-parser': 3.0.10 + '@smithy/hash-node': 3.0.7 + '@smithy/invalid-dependency': 3.0.7 + '@smithy/middleware-content-length': 3.0.9 + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/middleware-stack': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/node-http-handler': 3.2.4 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.28 - '@smithy/util-defaults-mode-node': 3.0.28 - '@smithy/util-endpoints': 2.1.6 - '@smithy/util-middleware': 3.0.10 - '@smithy/util-retry': 3.0.10 + '@smithy/util-defaults-mode-browser': 3.0.23 + '@smithy/util-defaults-mode-node': 3.0.23 + '@smithy/util-endpoints': 2.1.3 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 transitivePeerDependencies: @@ -8095,11 +8195,11 @@ snapshots: '@aws-sdk/core@3.609.0': dependencies: - '@smithy/core': 2.5.4 - '@smithy/protocol-http': 4.1.7 + '@smithy/core': 2.4.8 + '@smithy/protocol-http': 4.1.4 '@smithy/signature-v4': 3.1.2 - '@smithy/smithy-client': 3.4.5 - '@smithy/types': 3.7.1 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 fast-xml-parser: 4.2.5 tslib: 2.6.3 @@ -8121,8 +8221,8 @@ snapshots: dependencies: '@aws-sdk/client-cognito-identity': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt @@ -8130,8 +8230,8 @@ snapshots: '@aws-sdk/credential-provider-env@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-env@3.693.0': @@ -8146,12 +8246,12 @@ snapshots: dependencies: '@aws-sdk/types': 3.609.0 '@smithy/fetch-http-handler': 3.2.9 - '@smithy/node-http-handler': 3.3.1 - '@smithy/property-provider': 3.1.10 - '@smithy/protocol-http': 4.1.7 - '@smithy/smithy-client': 3.4.5 - '@smithy/types': 3.7.1 - '@smithy/util-stream': 3.3.1 + '@smithy/node-http-handler': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 tslib: 2.6.3 '@aws-sdk/credential-provider-http@3.693.0': @@ -8176,10 +8276,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.7 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8194,10 +8294,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.7 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8212,10 +8312,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0 '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.7 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8250,10 +8350,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.7 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8269,10 +8369,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.7 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8288,10 +8388,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0 '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.2.7 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8321,9 +8421,9 @@ snapshots: '@aws-sdk/credential-provider-process@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-process@3.693.0': @@ -8340,9 +8440,9 @@ snapshots: '@aws-sdk/client-sso': 3.609.0 '@aws-sdk/token-providers': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8354,9 +8454,9 @@ snapshots: '@aws-sdk/client-sso': 3.609.0 '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.609.0(@aws-sdk/client-sts@3.609.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8367,9 +8467,9 @@ snapshots: '@aws-sdk/client-sso': 3.609.0 '@aws-sdk/token-providers': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8393,8 +8493,8 @@ snapshots: dependencies: '@aws-sdk/client-sts': 3.609.0 '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/credential-provider-web-identity@3.693.0(@aws-sdk/client-sts@3.693.0)': @@ -8420,9 +8520,9 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0 '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.1.3 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8443,9 +8543,9 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0)) '@aws-sdk/credential-provider-web-identity': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/credential-provider-imds': 3.1.3 - '@smithy/property-provider': 3.1.3 - '@smithy/types': 3.7.1 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' @@ -8501,8 +8601,8 @@ snapshots: '@aws-sdk/middleware-host-header@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.7 - '@smithy/types': 3.7.1 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-host-header@3.693.0': @@ -8521,7 +8621,7 @@ snapshots: '@aws-sdk/middleware-logger@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-logger@3.693.0': @@ -8533,8 +8633,8 @@ snapshots: '@aws-sdk/middleware-recursion-detection@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/protocol-http': 4.1.7 - '@smithy/types': 3.7.1 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-recursion-detection@3.693.0': @@ -8580,8 +8680,8 @@ snapshots: dependencies: '@aws-sdk/types': 3.609.0 '@aws-sdk/util-endpoints': 3.609.0 - '@smithy/protocol-http': 4.1.7 - '@smithy/types': 3.7.1 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/middleware-user-agent@3.693.0': @@ -8597,10 +8697,10 @@ snapshots: '@aws-sdk/region-config-resolver@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.10 + '@smithy/util-middleware': 3.0.7 tslib: 2.6.3 '@aws-sdk/region-config-resolver@3.693.0': @@ -8635,9 +8735,9 @@ snapshots: '@aws-sdk/token-providers@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 optional: true @@ -8645,18 +8745,18 @@ snapshots: dependencies: '@aws-sdk/client-sso-oidc': 3.609.0(@aws-sdk/client-sts@3.609.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.609.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': dependencies: '@aws-sdk/client-sso-oidc': 3.693.0(@aws-sdk/client-sts@3.693.0) '@aws-sdk/types': 3.609.0 - '@smithy/property-provider': 3.1.10 - '@smithy/shared-ini-file-loader': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/token-providers@3.693.0(@aws-sdk/client-sso-oidc@3.693.0(@aws-sdk/client-sts@3.693.0))': @@ -8670,7 +8770,12 @@ snapshots: '@aws-sdk/types@3.609.0': dependencies: - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@aws-sdk/types@3.667.0': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/types@3.692.0': @@ -8690,8 +8795,8 @@ snapshots: '@aws-sdk/util-endpoints@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.7.1 - '@smithy/util-endpoints': 2.1.6 + '@smithy/types': 3.5.0 + '@smithy/util-endpoints': 2.1.3 tslib: 2.6.3 '@aws-sdk/util-endpoints@3.693.0': @@ -8705,7 +8810,7 @@ snapshots: dependencies: '@aws-sdk/types': 3.609.0 '@smithy/querystring-builder': 3.0.3 - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-format-url@3.693.0': @@ -8722,7 +8827,7 @@ snapshots: '@aws-sdk/util-user-agent-browser@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 bowser: 2.11.0 tslib: 2.6.3 @@ -8736,8 +8841,8 @@ snapshots: '@aws-sdk/util-user-agent-node@3.609.0': dependencies: '@aws-sdk/types': 3.609.0 - '@smithy/node-config-provider': 3.1.11 - '@smithy/types': 3.7.1 + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@aws-sdk/util-user-agent-node@3.693.0': @@ -9164,7 +9269,7 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.11-d': + '@pagopa/interop-outbound-models@1.0.11-e': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 @@ -9303,6 +9408,11 @@ snapshots: '@sinonjs/text-encoding@0.7.3': {} + '@smithy/abort-controller@3.1.5': + dependencies: + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/abort-controller@3.1.8': dependencies: '@smithy/types': 3.7.1 @@ -9325,6 +9435,27 @@ snapshots: '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 + '@smithy/config-resolver@3.0.9': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/util-config-provider': 3.0.0 + '@smithy/util-middleware': 3.0.7 + tslib: 2.6.3 + + '@smithy/core@2.4.8': + dependencies: + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-retry': 3.0.23 + '@smithy/middleware-serde': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + '@smithy/core@2.5.4': dependencies: '@smithy/middleware-serde': 3.0.10 @@ -9336,12 +9467,12 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/credential-provider-imds@3.1.3': + '@smithy/credential-provider-imds@3.2.4': dependencies: - '@smithy/node-config-provider': 3.1.11 - '@smithy/property-provider': 3.1.10 - '@smithy/types': 3.7.1 - '@smithy/url-parser': 3.0.10 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 tslib: 2.6.3 '@smithy/credential-provider-imds@3.2.7': @@ -9384,9 +9515,9 @@ snapshots: '@smithy/fetch-http-handler@3.2.9': dependencies: - '@smithy/protocol-http': 4.1.7 - '@smithy/querystring-builder': 3.0.10 - '@smithy/types': 3.7.1 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 '@smithy/util-base64': 3.0.0 tslib: 2.6.3 @@ -9412,9 +9543,9 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 - '@smithy/hash-node@3.0.3': + '@smithy/hash-node@3.0.7': dependencies: - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 '@smithy/util-buffer-from': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -9430,9 +9561,9 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/invalid-dependency@3.0.3': + '@smithy/invalid-dependency@3.0.7': dependencies: - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/is-array-buffer@2.2.0': @@ -9455,6 +9586,22 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/middleware-content-length@3.0.9': + dependencies: + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/middleware-endpoint@3.1.4': + dependencies: + '@smithy/middleware-serde': 3.0.7 + '@smithy/node-config-provider': 3.1.8 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 + '@smithy/url-parser': 3.0.7 + '@smithy/util-middleware': 3.0.7 + tslib: 2.6.3 + '@smithy/middleware-endpoint@3.2.4': dependencies: '@smithy/core': 2.5.4 @@ -9466,6 +9613,18 @@ snapshots: '@smithy/util-middleware': 3.0.10 tslib: 2.6.3 + '@smithy/middleware-retry@3.0.23': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/protocol-http': 4.1.4 + '@smithy/service-error-classification': 3.0.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + '@smithy/util-middleware': 3.0.7 + '@smithy/util-retry': 3.0.7 + tslib: 2.6.3 + uuid: 9.0.1 + '@smithy/middleware-retry@3.0.28': dependencies: '@smithy/node-config-provider': 3.1.11 @@ -9483,11 +9642,21 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/middleware-serde@3.0.7': + dependencies: + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/middleware-stack@3.0.10': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/middleware-stack@3.0.7': + dependencies: + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/node-config-provider@3.1.11': dependencies: '@smithy/property-provider': 3.1.10 @@ -9495,6 +9664,21 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/node-config-provider@3.1.8': + dependencies: + '@smithy/property-provider': 3.1.7 + '@smithy/shared-ini-file-loader': 3.1.8 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/node-http-handler@3.2.4': + dependencies: + '@smithy/abort-controller': 3.1.5 + '@smithy/protocol-http': 4.1.4 + '@smithy/querystring-builder': 3.0.7 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/node-http-handler@3.3.1': dependencies: '@smithy/abort-controller': 3.1.8 @@ -9508,9 +9692,14 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 - '@smithy/property-provider@3.1.3': + '@smithy/property-provider@3.1.7': dependencies: - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/protocol-http@4.1.4': + dependencies: + '@smithy/types': 3.5.0 tslib: 2.6.3 '@smithy/protocol-http@4.1.7': @@ -9526,7 +9715,13 @@ snapshots: '@smithy/querystring-builder@3.0.3': dependencies: - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 + '@smithy/util-uri-escape': 3.0.0 + tslib: 2.6.3 + + '@smithy/querystring-builder@3.0.7': + dependencies: + '@smithy/types': 3.5.0 '@smithy/util-uri-escape': 3.0.0 tslib: 2.6.3 @@ -9535,15 +9730,29 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/querystring-parser@3.0.7': + dependencies: + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/service-error-classification@3.0.10': dependencies: '@smithy/types': 3.7.1 + '@smithy/service-error-classification@3.0.7': + dependencies: + '@smithy/types': 3.5.0 + '@smithy/shared-ini-file-loader@3.1.11': dependencies: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/shared-ini-file-loader@3.1.8': + dependencies: + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/signature-v4@2.3.0': dependencies: '@smithy/is-array-buffer': 2.2.0 @@ -9557,9 +9766,9 @@ snapshots: '@smithy/signature-v4@3.1.2': dependencies: '@smithy/is-array-buffer': 3.0.0 - '@smithy/types': 3.7.1 + '@smithy/types': 3.5.0 '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.10 + '@smithy/util-middleware': 3.0.7 '@smithy/util-uri-escape': 3.0.0 '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 @@ -9575,6 +9784,15 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.6.3 + '@smithy/smithy-client@3.4.0': + dependencies: + '@smithy/middleware-endpoint': 3.1.4 + '@smithy/middleware-stack': 3.0.7 + '@smithy/protocol-http': 4.1.4 + '@smithy/types': 3.5.0 + '@smithy/util-stream': 3.1.9 + tslib: 2.6.3 + '@smithy/smithy-client@3.4.5': dependencies: '@smithy/core': 2.5.4 @@ -9589,6 +9807,10 @@ snapshots: dependencies: tslib: 2.6.3 + '@smithy/types@3.5.0': + dependencies: + tslib: 2.6.3 + '@smithy/types@3.7.1': dependencies: tslib: 2.6.3 @@ -9599,6 +9821,12 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/url-parser@3.0.7': + dependencies: + '@smithy/querystring-parser': 3.0.7 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/util-base64@3.0.0': dependencies: '@smithy/util-buffer-from': 3.0.0 @@ -9627,6 +9855,14 @@ snapshots: dependencies: tslib: 2.6.3 + '@smithy/util-defaults-mode-browser@3.0.23': + dependencies: + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + bowser: 2.11.0 + tslib: 2.6.3 + '@smithy/util-defaults-mode-browser@3.0.28': dependencies: '@smithy/property-provider': 3.1.10 @@ -9635,6 +9871,16 @@ snapshots: bowser: 2.11.0 tslib: 2.6.3 + '@smithy/util-defaults-mode-node@3.0.23': + dependencies: + '@smithy/config-resolver': 3.0.9 + '@smithy/credential-provider-imds': 3.2.4 + '@smithy/node-config-provider': 3.1.8 + '@smithy/property-provider': 3.1.7 + '@smithy/smithy-client': 3.4.0 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/util-defaults-mode-node@3.0.28': dependencies: '@smithy/config-resolver': 3.0.12 @@ -9645,6 +9891,12 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/util-endpoints@2.1.3': + dependencies: + '@smithy/node-config-provider': 3.1.8 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/util-endpoints@2.1.6': dependencies: '@smithy/node-config-provider': 3.1.11 @@ -9669,12 +9921,34 @@ snapshots: '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/util-middleware@3.0.7': + dependencies: + '@smithy/types': 3.5.0 + tslib: 2.6.3 + '@smithy/util-retry@3.0.10': dependencies: '@smithy/service-error-classification': 3.0.10 '@smithy/types': 3.7.1 tslib: 2.6.3 + '@smithy/util-retry@3.0.7': + dependencies: + '@smithy/service-error-classification': 3.0.7 + '@smithy/types': 3.5.0 + tslib: 2.6.3 + + '@smithy/util-stream@3.1.9': + dependencies: + '@smithy/fetch-http-handler': 3.2.9 + '@smithy/node-http-handler': 3.2.4 + '@smithy/types': 3.5.0 + '@smithy/util-base64': 3.0.0 + '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-hex-encoding': 3.0.0 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + '@smithy/util-stream@3.3.1': dependencies: '@smithy/fetch-http-handler': 4.1.1 @@ -10387,7 +10661,7 @@ snapshots: get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 - type-detect: 4.0.8 + type-detect: 4.1.0 chalk@2.4.2: dependencies: @@ -10549,18 +10823,18 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - csv-generate@4.4.2: {} + csv-generate@4.4.1: {} - csv-parse@5.6.0: {} + csv-parse@5.5.6: {} - csv-stringify@6.5.2: {} + csv-stringify@6.5.1: {} csv@6.3.2: dependencies: - csv-generate: 4.4.2 - csv-parse: 5.6.0 - csv-stringify: 6.5.2 - stream-transform: 3.3.3 + csv-generate: 4.4.1 + csv-parse: 5.5.6 + csv-stringify: 6.5.1 + stream-transform: 3.3.2 data-uri-to-buffer@6.0.2: {} @@ -12699,7 +12973,7 @@ snapshots: std-env@3.7.0: {} - stream-transform@3.3.3: {} + stream-transform@3.3.2: {} streamsearch@1.1.0: {} From 1629ec3f81c4a3ca83d6a192edbe53c6517fb023 Mon Sep 17 00:00:00 2001 From: Vittorio Caprio Date: Thu, 12 Dec 2024 10:27:17 +0100 Subject: [PATCH 081/126] PIN-5817 Fix getPurposes for delegated eservice and show risk analysis (#1284) --- .../src/services/purposeService.ts | 22 ++- .../src/services/readModelService.ts | 40 ++++- .../purpose-process/test/getPurposes.test.ts | 153 ++++++++++++++++++ 3 files changed, 212 insertions(+), 3 deletions(-) diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index e27b410a64..a1f65b3e81 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -43,6 +43,7 @@ import { CorrelationId, delegationKind, Delegation, + DelegationKind, } from "pagopa-interop-models"; import { purposeApi } from "pagopa-interop-api-clients"; import { P, match } from "ts-pattern"; @@ -219,6 +220,14 @@ async function retrieveTenantKind( return tenant.kind; } +async function retrieveActiveDelegation( + eserviceId: EServiceId, + delegationKind: DelegationKind, + readModelService: ReadModelService +): Promise { + return readModelService.getActiveDelegation(eserviceId, delegationKind); +} + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function purposeServiceBuilder( dbInstance: DB, @@ -623,18 +632,27 @@ export function purposeServiceBuilder( if (eservice === undefined) { throw eserviceNotFound(purpose.eserviceId); } + + const activeProducerDelegation = await retrieveActiveDelegation( + eservice.id, + delegationKind.delegatedProducer, + readModelService + ); + return { purpose, eservice, + activeProducerDelegation, }; }) ); const purposesToReturn = mappingPurposeEservice.map( - ({ purpose, eservice }) => { + ({ purpose, eservice, activeProducerDelegation }) => { const isProducerOrConsumer = organizationId === purpose.consumerId || - organizationId === eservice.producerId; + organizationId === eservice.producerId || + organizationId === activeProducerDelegation?.delegateId; return { ...purpose, diff --git a/packages/purpose-process/src/services/readModelService.ts b/packages/purpose-process/src/services/readModelService.ts index 2b17ecda7c..3651e52a83 100644 --- a/packages/purpose-process/src/services/readModelService.ts +++ b/packages/purpose-process/src/services/readModelService.ts @@ -26,6 +26,7 @@ import { delegationState, Delegation, DelegationKind, + delegationKind, } from "pagopa-interop-models"; import { Document, Filter, WithId } from "mongodb"; import { z } from "zod"; @@ -176,10 +177,47 @@ async function buildGetPurposesAggregation( } : {}; + const eserviceIdsFilters = + producersIds.length > 0 + ? [ + { + $lookup: { + from: "delegations", + localField: "data.id", + foreignField: "data.eserviceId", + as: "matchingDelegations", + }, + }, + { + $match: { + $or: [ + { "data.producerId": { $in: producersIds } }, + { + $and: [ + { + "matchingDelegations.data.delegateId": { + $in: producersIds, + }, + }, + { + "matchingDelegations.data.state": delegationState.active, + }, + { + "matchingDelegations.data.kind": + delegationKind.delegatedProducer, + }, + ], + }, + ], + }, + }, + ] + : []; + const producerEServicesIds = producersIds.length > 0 ? await eservices - .find({ "data.producerId": { $in: producersIds } }) + .aggregate(eserviceIdsFilters) .toArray() .then((results) => results.map((eservice) => eservice.data.id.toString()) diff --git a/packages/purpose-process/test/getPurposes.test.ts b/packages/purpose-process/test/getPurposes.test.ts index 4409eec92e..a2ee002bde 100644 --- a/packages/purpose-process/test/getPurposes.test.ts +++ b/packages/purpose-process/test/getPurposes.test.ts @@ -1,11 +1,15 @@ import { EService, + EServiceId, Purpose, TenantId, + delegationKind, + delegationState, generateId, purposeVersionState, tenantKind, toReadModelEService, + unsafeBrandId, } from "pagopa-interop-models"; import { beforeEach, describe, expect, it } from "vitest"; import { @@ -13,10 +17,15 @@ import { getMockPurpose, writeInReadmodel, getMockValidRiskAnalysisForm, + getMockDelegation, + getMockTenant, } from "pagopa-interop-commons-test/index.js"; import { genericLogger } from "pagopa-interop-commons"; import { + addOneDelegation, + addOneEService, addOnePurpose, + addOneTenant, eservices, getMockEService, purposeService, @@ -27,20 +36,24 @@ describe("getPurposes", async () => { const producerId1: TenantId = generateId(); const producerId2: TenantId = generateId(); const consumerId1: TenantId = generateId(); + const delegateId: TenantId = generateId(); const mockEService1ByTenant1: EService = { ...getMockEService(), producerId: producerId1, + name: "eService 1", }; const mockEService2ByTenant1: EService = { ...getMockEService(), producerId: producerId1, + name: "eService 2", }; const mockEService3ByTenant2: EService = { ...getMockEService(), producerId: producerId2, + name: "eService 3", }; const mockEService4 = getMockEService(); @@ -513,4 +526,144 @@ describe("getPurposes", async () => { expect(result.totalCount).toBe(0); expect(result.results).toEqual([]); }); + + describe("Producer Delegation active for provided producerIds filter", async () => { + it("should get the purposes if they exist (parameters: producersIds with only delegateId)", async () => { + const organizationId = generateId(); + const delegate = getMockTenant(delegateId); + await addOneTenant(delegate); + + const delegation = getMockDelegation({ + delegateId, + state: delegationState.active, + eserviceId: mockPurpose2.eserviceId, + kind: delegationKind.delegatedProducer, + }); + await addOneDelegation(delegation); + + const revokedDelegation = getMockDelegation({ + delegateId, + state: delegationState.revoked, + eserviceId: mockPurpose3.eserviceId, + kind: delegationKind.delegatedProducer, + }); + await addOneDelegation(revokedDelegation); + + const results = await purposeService.getPurposes( + organizationId, // irrelevant to check retrieved purposes + { + eservicesIds: [], + consumersIds: [], + producersIds: [delegateId], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + + expect(results.totalCount).toBe(2); + expect(results.results).toEqual([mockPurpose1, mockPurpose2]); + }); + it("should get the purposes if they exist (parameters: producersIds that contains delegateId and generic producerId)", async () => { + const delegate = getMockTenant(delegateId); + await addOneTenant(delegate); + + const delegation = getMockDelegation({ + delegateId, + state: delegationState.active, + eserviceId: mockPurpose2.eserviceId, + kind: delegationKind.delegatedProducer, + }); + await addOneDelegation(delegation); + + const revokedDelegation = getMockDelegation({ + delegateId, + state: delegationState.revoked, + eserviceId: mockPurpose3.eserviceId, + kind: delegationKind.delegatedProducer, + }); + await addOneDelegation(revokedDelegation); + + const results = await purposeService.getPurposes( + generateId(), + { + eservicesIds: [], + consumersIds: [], + producersIds: [delegateId, producerId2], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + + expect(results.totalCount).toBe(4); + expect(results.results).toEqual([ + mockPurpose1, + mockPurpose2, + mockPurpose4, + mockPurpose6, + ]); + }); + }); + + describe("Producer Delegation active getPurposes return also risk analysis data", async () => { + it("should get the purposes and also risk analysis if requester is a delegate", async () => { + const riskAnalysisForm = getMockValidRiskAnalysisForm(tenantKind.PA); + const producerId = generateId(); + await addOneTenant(getMockTenant(producerId)); + + const eservice = { + ...getMockEService(), + id: unsafeBrandId("6A7A8CC9-02B0-4AC3-862D-9CDFB102A181"), + producerId, + }; + await addOneEService(eservice); + + const mockPurpose10 = { + ...getMockPurpose(), + title: "purpose 10", + riskAnalysisForm, + eserviceId: eservice.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + await addOnePurpose(mockPurpose10); + const mockPurpose11 = { + ...getMockPurpose(), + title: "purpose 11", + riskAnalysisForm, + eserviceId: eservice.id, + versions: [getMockPurposeVersion(purposeVersionState.active)], + }; + await addOnePurpose(mockPurpose11); + + const delegate = getMockTenant(delegateId); + await addOneTenant(delegate); + + const delegation = getMockDelegation({ + delegateId, + state: delegationState.active, + eserviceId: eservice.id, + kind: delegationKind.delegatedProducer, + }); + await addOneDelegation(delegation); + + const results = await purposeService.getPurposes( + delegateId, + { + eservicesIds: [], + consumersIds: [], + producersIds: [producerId], + states: [], + excludeDraft: false, + }, + { offset: 0, limit: 50 }, + genericLogger + ); + + expect(results.totalCount).toBe(2); + expect(results.results).toEqual([mockPurpose10, mockPurpose11]); + }); + }); }); From 3d5e7c64d3e38811921a5c2d48289834c40ef007 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 12 Dec 2024 10:46:01 +0100 Subject: [PATCH 082/126] Updated delegation contract pretty names (#1286) --- .../src/services/delegationContractBuilder.ts | 22 +++++++++++++++---- .../test/approveProducerDelegation.test.ts | 3 ++- .../test/revokeProducerDelegation.test.ts | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/delegation-process/src/services/delegationContractBuilder.ts b/packages/delegation-process/src/services/delegationContractBuilder.ts index d55200ad73..21db2d2783 100644 --- a/packages/delegation-process/src/services/delegationContractBuilder.ts +++ b/packages/delegation-process/src/services/delegationContractBuilder.ts @@ -24,8 +24,16 @@ import { import { assertStampExists } from "./validators.js"; const CONTENT_TYPE_PDF = "application/pdf"; -const DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME = "Delega"; -const DELEGATION_REVOCATION_CONTRACT_PRETTY_NAME = "Revoca della delega"; + +const createDelegationContractPrettyName = ( + eServiceName: string, + documentType: "activation" | "revocation" +): string => { + const prettyName = `${ + documentType === "activation" ? "Delega" : "Revoca_Delega" + }_${eServiceName}`; + return prettyName.length > 45 ? prettyName.slice(0, 45) : prettyName; +}; const createDelegationDocumentName = ( documentCreatedAt: Date, @@ -117,7 +125,10 @@ export const contractBuilder = { return { id: documentId, name: documentName, - prettyName: DELEGATION_ACTIVATION_CONTRACT_PRETTY_NAME, + prettyName: createDelegationContractPrettyName( + eservice.name, + "activation" + ), contentType: CONTENT_TYPE_PDF, path: documentPath, createdAt: documentCreatedAt, @@ -196,7 +207,10 @@ export const contractBuilder = { return { id: documentId, name: documentName, - prettyName: DELEGATION_REVOCATION_CONTRACT_PRETTY_NAME, + prettyName: createDelegationContractPrettyName( + eservice.name, + "revocation" + ), contentType: CONTENT_TYPE_PDF, path: documentPath, createdAt: documentCreatedAt, diff --git a/packages/delegation-process/test/approveProducerDelegation.test.ts b/packages/delegation-process/test/approveProducerDelegation.test.ts index 2b7a4f7f9e..29998f2820 100644 --- a/packages/delegation-process/test/approveProducerDelegation.test.ts +++ b/packages/delegation-process/test/approveProducerDelegation.test.ts @@ -102,13 +102,14 @@ describe("approve producer delegation", () => { const expectedContractName = `${formatDateyyyyMMddHHmmss( currentExecutionTime )}_delegation_activation_contract.pdf`; + const expectedContract = { id: expectedContractId, contentType: "application/pdf", createdAt: currentExecutionTime, name: expectedContractName, path: `${config.delegationDocumentPath}/${delegation.id}/${expectedContractId}/${expectedContractName}`, - prettyName: "Delega", + prettyName: `Delega_${eservice.name}`, }; expect( diff --git a/packages/delegation-process/test/revokeProducerDelegation.test.ts b/packages/delegation-process/test/revokeProducerDelegation.test.ts index 904cdcab27..ef502a7416 100644 --- a/packages/delegation-process/test/revokeProducerDelegation.test.ts +++ b/packages/delegation-process/test/revokeProducerDelegation.test.ts @@ -135,7 +135,7 @@ describe("revoke producer delegation", () => { createdAt: currentExecutionTime, name: expectedContractName, path: `${config.delegationDocumentPath}/${existentDelegation.id}/${expectedContractId}/${expectedContractName}`, - prettyName: "Revoca della delega", + prettyName: `Revoca_Delega_${eservice.name}`, }; expect( From c50d14f3eb7119b26d5ef49fda913ee3f512c418 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Thu, 12 Dec 2024 16:01:32 +0100 Subject: [PATCH 083/126] Fix `getEServices` visibility filter with producer delegation (#1292) --- .../src/services/readModelService.ts | 53 ++++++++++++------ .../catalog-process/test/getEservices.test.ts | 54 +++++++++++++++++++ 2 files changed, 91 insertions(+), 16 deletions(-) diff --git a/packages/catalog-process/src/services/readModelService.ts b/packages/catalog-process/src/services/readModelService.ts index 133143a7f9..87a2cd0100 100644 --- a/packages/catalog-process/src/services/readModelService.ts +++ b/packages/catalog-process/src/services/readModelService.ts @@ -154,19 +154,14 @@ export function readModelServiceBuilder( "data.id": { $in: ids }, }); - const delegationLookup = - producersIds.length > 0 || delegated !== undefined - ? [ - { - $lookup: { - from: "delegations", - localField: "data.id", - foreignField: "data.eserviceId", - as: "delegations", - }, - }, - ] - : []; + const delegationLookup = { + $lookup: { + from: "delegations", + localField: "data.id", + foreignField: "data.eserviceId", + as: "delegations", + }, + }; const producersIdsFilter = ReadModelRepository.arrayToFilter( producersIds, @@ -224,13 +219,39 @@ export function readModelServiceBuilder( $nor: [ { $and: [ - { "data.producerId": { $ne: authData.organizationId } }, + { + $nor: [ + { "data.producerId": authData.organizationId }, + { + delegations: { + $elemMatch: { + "data.delegateId": authData.organizationId, + "data.state": delegationState.active, + "data.kind": delegationKind.delegatedProducer, + }, + }, + }, + ], + }, { "data.descriptors": { $size: 0 } }, ], }, { $and: [ - { "data.producerId": { $ne: authData.organizationId } }, + { + $nor: [ + { "data.producerId": authData.organizationId }, + { + delegations: { + $elemMatch: { + "data.delegateId": authData.organizationId, + "data.state": delegationState.active, + "data.kind": delegationKind.delegatedProducer, + }, + }, + }, + ], + }, { "data.descriptors": { $size: 1 } }, { "data.descriptors.state": { @@ -292,7 +313,7 @@ export function readModelServiceBuilder( .otherwise(() => ({})); const aggregationPipeline = [ - ...delegationLookup, + delegationLookup, { $match: nameFilter }, { $match: idsFilter }, { $match: producersIdsFilter }, diff --git a/packages/catalog-process/test/getEservices.test.ts b/packages/catalog-process/test/getEservices.test.ts index b431865a42..9f79bc904f 100644 --- a/packages/catalog-process/test/getEservices.test.ts +++ b/packages/catalog-process/test/getEservices.test.ts @@ -1214,6 +1214,60 @@ describe("get eservices", () => { ]); } ); + it.each([descriptorState.draft, descriptorState.waitingForApproval])( + "should include eservices whose only descriptor is %s (requester is delegate, admin)", + async (state) => { + const descriptor9: Descriptor = { + ...mockDescriptor, + id: generateId(), + interface: mockDocument, + publishedAt: new Date(), + state, + }; + const eservice9: EService = { + ...mockEService, + id: generateId(), + name: "eservice 008", + producerId: organizationId1, + descriptors: [descriptor9], + }; + const delegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegateId: organizationId2, + eserviceId: eservice9.id, + state: delegationState.active, + }); + const authData: AuthData = { + ...getMockAuthData(organizationId2), + userRoles: [userRoles.ADMIN_ROLE], + }; + await addOneEService(eservice9); + await addOneDelegation(delegation); + const result = await catalogService.getEServices( + authData, + { + eservicesIds: [], + producersIds: [], + states: [], + agreementStates: [], + attributesIds: [], + }, + 0, + 50, + genericLogger + ); + expect(result.totalCount).toBe(7); + expect(result.results).toEqual([ + eservice1, + eservice2, + eservice3, + eservice4, + eservice5, + eservice6, + eservice9, + ]); + } + ); it.each([descriptorState.draft, descriptorState.waitingForApproval])( "should not include eservices whose only descriptor is %s (requester is the producer, not admin nor api, nor support)", async (state) => { From 2c64a8fb1982e01cb79a6dd04ab4eea575d169c7 Mon Sep 17 00:00:00 2001 From: Andrea Zerbini Date: Thu, 12 Dec 2024 16:44:32 +0100 Subject: [PATCH 084/126] Improve mockMessage type in tests (#1283) --- .../client-readmodel-writer/test/consumerServiceV1.test.ts | 4 +--- .../client-readmodel-writer/test/consumerServiceV2.test.ts | 4 +--- .../test/consumerServiceV2.test.ts | 4 +--- .../test/consumerServiceV2.test.ts | 4 +--- .../tenant-readmodel-writer/test/consumerServiceV2.test.ts | 4 +--- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/client-readmodel-writer/test/consumerServiceV1.test.ts b/packages/client-readmodel-writer/test/consumerServiceV1.test.ts index 7e2e3f3356..7de1510343 100644 --- a/packages/client-readmodel-writer/test/consumerServiceV1.test.ts +++ b/packages/client-readmodel-writer/test/consumerServiceV1.test.ts @@ -36,14 +36,12 @@ import { clients } from "./utils.js"; describe("Events V1", async () => { const mockClient = getMockClient(); - const mockMessage: AuthorizationEventEnvelopeV1 = { + const mockMessage: Omit = { event_version: 1, stream_id: mockClient.id, version: 1, sequence_num: 1, log_date: new Date(), - type: "ClientAdded", - data: {}, }; it("ClientAdded", async () => { diff --git a/packages/client-readmodel-writer/test/consumerServiceV2.test.ts b/packages/client-readmodel-writer/test/consumerServiceV2.test.ts index bcc0ec14af..911218a3b7 100644 --- a/packages/client-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/client-readmodel-writer/test/consumerServiceV2.test.ts @@ -26,14 +26,12 @@ import { clients } from "./utils.js"; describe("Events V2", async () => { const mockClient = getMockClient(); - const mockMessage: AuthorizationEventEnvelopeV2 = { + const mockMessage: Omit = { event_version: 2, stream_id: mockClient.id, version: 1, sequence_num: 1, log_date: new Date(), - type: "ClientAdded", - data: {}, }; it("ClientAdded", async () => { diff --git a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts index e9aa448762..9686e3370f 100644 --- a/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/delegation-readmodel-writer/test/consumerServiceV2.test.ts @@ -19,14 +19,12 @@ describe("Events V2", async () => { const mockDelegation = getMockDelegation({ kind: randomArrayItem(Object.values(delegationKind)), }); - const mockMessage: DelegationEventEnvelopeV2 = { + const mockMessage: Omit = { event_version: 2, stream_id: mockDelegation.id, version: 1, sequence_num: 1, log_date: new Date(), - type: "ProducerDelegationApproved", - data: {}, }; it("ProducerDelegationApproved", async () => { diff --git a/packages/producer-keychain-readmodel-writer/test/consumerServiceV2.test.ts b/packages/producer-keychain-readmodel-writer/test/consumerServiceV2.test.ts index 823bda600d..78a3466df8 100644 --- a/packages/producer-keychain-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/producer-keychain-readmodel-writer/test/consumerServiceV2.test.ts @@ -26,14 +26,12 @@ import { producerKeychains } from "./utils.js"; describe("Events V2", async () => { const mockProducerKeychain = getMockProducerKeychain(); - const mockMessage: AuthorizationEventEnvelopeV2 = { + const mockMessage: Omit = { event_version: 2, stream_id: mockProducerKeychain.id, version: 1, sequence_num: 1, log_date: new Date(), - type: "ProducerKeychainAdded", - data: {}, }; it("ProducerKeychainAdded", async () => { diff --git a/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts b/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts index c860c6abb4..23e5d92174 100644 --- a/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts +++ b/packages/tenant-readmodel-writer/test/consumerServiceV2.test.ts @@ -37,14 +37,12 @@ import { tenants } from "./utils.js"; describe("Tenant Events V2", async () => { const mockTenant = getMockTenant(); - const mockMessage: TenantEventEnvelopeV2 = { + const mockMessage: Omit = { event_version: 2, stream_id: mockTenant.id, version: 1, sequence_num: 1, log_date: new Date(), - type: "TenantOnboarded", - data: {}, }; it("TenantOnboarded", async () => { From c41ed7c629db22ad740a9e8daac31033d9ffa3a6 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 13 Dec 2024 11:20:17 +0100 Subject: [PATCH 085/126] Added `tenantKind` query param in get latest risk analysis query (#1293) --- packages/api-clients/open-api/bffApi.yml | 19 +++++++++++++++++++ .../src/api/catalogApiConverter.ts | 4 ++++ .../src/routers/purposeRouter.ts | 5 ++++- .../src/services/purposeService.ts | 12 +++++++----- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 9114bf29a2..32bf011560 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -10537,6 +10537,11 @@ paths: /purposes/riskAnalysis/latest: parameters: - $ref: "#/components/parameters/CorrelationIdHeader" + - name: tenantKind + in: query + required: false + schema: + $ref: "#/components/schemas/TenantKind" get: tags: - purposes @@ -13845,6 +13850,7 @@ components: - id - name - description + - producer - technology - mode - riskAnalysis @@ -13857,6 +13863,8 @@ components: type: string description: type: string + producer: + $ref: "#/components/schemas/ProducerDescriptorEServiceProducer" technology: $ref: "#/components/schemas/EServiceTechnology" mode: @@ -13875,6 +13883,17 @@ components: $ref: "#/components/schemas/Mail" isSignalHubEnabled: type: boolean + ProducerDescriptorEServiceProducer: + type: object + additionalProperties: false + required: + - id + properties: + id: + type: string + format: uuid + tenantKind: + $ref: "#/components/schemas/TenantKind" EServiceDoc: type: object additionalProperties: false diff --git a/packages/backend-for-frontend/src/api/catalogApiConverter.ts b/packages/backend-for-frontend/src/api/catalogApiConverter.ts index 142b236a74..d788ecadd8 100644 --- a/packages/backend-for-frontend/src/api/catalogApiConverter.ts +++ b/packages/backend-for-frontend/src/api/catalogApiConverter.ts @@ -254,6 +254,10 @@ export function toBffCatalogApiProducerDescriptorEService( name: eservice.name, description: eservice.description, technology: eservice.technology, + producer: { + id: producer.id, + tenantKind: producer.kind, + }, mode: eservice.mode, mail: producerMail && { address: producerMail.address, diff --git a/packages/backend-for-frontend/src/routers/purposeRouter.ts b/packages/backend-for-frontend/src/routers/purposeRouter.ts index 6b0a1985ab..fde2a18b80 100644 --- a/packages/backend-for-frontend/src/routers/purposeRouter.ts +++ b/packages/backend-for-frontend/src/routers/purposeRouter.ts @@ -434,7 +434,10 @@ const purposeRouter = ( try { const result = - await purposeService.retrieveLatestRiskAnalysisConfiguration(ctx); + await purposeService.retrieveLatestRiskAnalysisConfiguration( + req.query.tenantKind, + ctx + ); return res .status(200) diff --git a/packages/backend-for-frontend/src/services/purposeService.ts b/packages/backend-for-frontend/src/services/purposeService.ts index 7aac5e1d02..9bc499771e 100644 --- a/packages/backend-for-frontend/src/services/purposeService.ts +++ b/packages/backend-for-frontend/src/services/purposeService.ts @@ -624,15 +624,17 @@ export function purposeServiceBuilder( headers ); }, - async retrieveLatestRiskAnalysisConfiguration({ - headers, - logger, - }: WithLogger): Promise { + async retrieveLatestRiskAnalysisConfiguration( + tenantKind: bffApi.TenantKind | undefined, + { headers, logger }: WithLogger + ): Promise { logger.info(`Retrieving risk analysis latest configuration`); return await purposeProcessClient.retrieveLatestRiskAnalysisConfiguration( { - queries: undefined, + queries: { + tenantKind, + }, headers, } ); From 88598b872650cb9d83b6f630310442f741d14fef Mon Sep 17 00:00:00 2001 From: Vittorio Caprio Date: Fri, 13 Dec 2024 12:02:52 +0100 Subject: [PATCH 086/126] PIN-5777 Add Descriptor Reject Reasons (#1249) --- packages/backend-for-frontend/src/services/catalogService.ts | 1 + packages/catalog-process/src/services/catalogService.ts | 2 ++ packages/catalog-process/test/utils.ts | 1 + packages/commons-test/src/testUtils.ts | 2 ++ 4 files changed, 6 insertions(+) diff --git a/packages/backend-for-frontend/src/services/catalogService.ts b/packages/backend-for-frontend/src/services/catalogService.ts index 4ecf5d9539..9bd1b1303b 100644 --- a/packages/backend-for-frontend/src/services/catalogService.ts +++ b/packages/backend-for-frontend/src/services/catalogService.ts @@ -312,6 +312,7 @@ export function catalogServiceBuilder( eservice, producerTenant ), + rejectionReasons: descriptor.rejectionReasons, }; }, getProducerEServiceDetails: async ( diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 4d976a99e6..288da4bad1 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -528,6 +528,7 @@ export function catalogServiceBuilder( archivedAt: undefined, createdAt: creationDate, attributes: { certified: [], declared: [], verified: [] }, + rejectionReasons: undefined, }; const eserviceWithDescriptor: EService = { @@ -1001,6 +1002,7 @@ export function catalogServiceBuilder( archivedAt: undefined, createdAt: new Date(), attributes: parsedAttributes, + rejectionReasons: undefined, }; const newEservice: EService = { diff --git a/packages/catalog-process/test/utils.ts b/packages/catalog-process/test/utils.ts index 707be43610..b3023c401e 100644 --- a/packages/catalog-process/test/utils.ts +++ b/packages/catalog-process/test/utils.ts @@ -170,6 +170,7 @@ export const getMockDescriptor = (state?: DescriptorState): Descriptor => ({ ...(state === descriptorState.suspended ? { suspendedAt: new Date() } : {}), ...(state === descriptorState.deprecated ? { deprecatedAt: new Date() } : {}), ...(state === descriptorState.published ? { publishedAt: new Date() } : {}), + rejectionReasons: [], }); export const getMockEServiceAttribute = (): EServiceAttribute => ({ diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index 706be8db76..edde65239a 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -143,6 +143,7 @@ export const getMockDescriptorPublished = ( declared: declaredAttributes, verified: verifiedAttributes, }, + rejectionReasons: undefined, }); export const getMockEServiceAttribute = ( @@ -324,6 +325,7 @@ export const getMockDescriptor = (state?: DescriptorState): Descriptor => ({ verified: [], declared: [], }, + rejectionReasons: undefined, }); export const getMockDescriptorList = (length?: number): Descriptor[] => { From 888bbeb7877cfe20cc8914d87e40996cb219b586 Mon Sep 17 00:00:00 2001 From: Eric Camellini Date: Fri, 13 Dec 2024 15:52:54 +0100 Subject: [PATCH 087/126] Added missing delegation param to agreement activation (#1294) --- .../src/services/agreementActivationProcessor.ts | 11 ++++++----- .../src/services/agreementService.ts | 2 +- .../src/services/agreementStampUtils.ts | 2 +- .../test/activateAgreement.test.ts | 15 ++++++++++++++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/agreement-process/src/services/agreementActivationProcessor.ts b/packages/agreement-process/src/services/agreementActivationProcessor.ts index ff33a9d960..228f18222f 100644 --- a/packages/agreement-process/src/services/agreementActivationProcessor.ts +++ b/packages/agreement-process/src/services/agreementActivationProcessor.ts @@ -6,7 +6,7 @@ import { AgreementEventV2, AgreementState, CorrelationId, - DelegationId, + Delegation, Descriptor, EService, Tenant, @@ -49,7 +49,7 @@ export function createActivationUpdateAgreementSeed({ suspendedByConsumer, suspendedByProducer, suspendedByPlatform, - producerDelegationId, + producerDelegation, }: { isFirstActivation: boolean; newState: AgreementState; @@ -61,9 +61,9 @@ export function createActivationUpdateAgreementSeed({ suspendedByConsumer: boolean | undefined; suspendedByProducer: boolean | undefined; suspendedByPlatform: boolean | undefined; - producerDelegationId?: DelegationId | undefined; + producerDelegation: Delegation | undefined; }): UpdateAgreementSeed { - const stamp = createStamp(authData.userId, producerDelegationId); + const stamp = createStamp(authData.userId, producerDelegation?.id); return isFirstActivation ? { @@ -99,7 +99,8 @@ export function createActivationUpdateAgreementSeed({ agreement, authData.organizationId, agreementState.active, - stamp + stamp, + producerDelegation?.delegateId ), }, suspendedByPlatform, diff --git a/packages/agreement-process/src/services/agreementService.ts b/packages/agreement-process/src/services/agreementService.ts index 959d325bee..a7c59cfe48 100644 --- a/packages/agreement-process/src/services/agreementService.ts +++ b/packages/agreement-process/src/services/agreementService.ts @@ -1081,7 +1081,7 @@ export function agreementServiceBuilder( suspendedByConsumer, suspendedByProducer, suspendedByPlatform, - producerDelegationId: activeProducerDelegation?.id, + producerDelegation: activeProducerDelegation, }); const updatedAgreementWithoutContract: Agreement = { diff --git a/packages/agreement-process/src/services/agreementStampUtils.ts b/packages/agreement-process/src/services/agreementStampUtils.ts index 5505316e02..612dd2badc 100644 --- a/packages/agreement-process/src/services/agreementStampUtils.ts +++ b/packages/agreement-process/src/services/agreementStampUtils.ts @@ -35,7 +35,7 @@ export const suspendedByProducerStamp = ( requesterOrgId: TenantId, destinationState: AgreementState, stamp: AgreementStamp, - delegateProducerId?: TenantId | undefined + delegateProducerId: TenantId | undefined ): AgreementStamp | undefined => match<[TenantId | undefined, AgreementState]>([ requesterOrgId, diff --git a/packages/agreement-process/test/activateAgreement.test.ts b/packages/agreement-process/test/activateAgreement.test.ts index c3370b058c..8a6380d611 100644 --- a/packages/agreement-process/test/activateAgreement.test.ts +++ b/packages/agreement-process/test/activateAgreement.test.ts @@ -898,6 +898,7 @@ describe("activate agreement", () => { consumerId: consumer.id, descriptors: [getMockDescriptorPublished()], }; + const mockAgreement = getMockAgreement(eservice.id); const agreement: Agreement = { ...getMockAgreement(eservice.id), state: agreementState.suspended, @@ -906,8 +907,15 @@ describe("activate agreement", () => { consumerId: consumer.id, suspendedAt: new Date(), suspendedByConsumer: false, - suspendedByProducer: false, + suspendedByProducer: true, suspendedByPlatform: false, + stamps: { + ...mockAgreement.stamps, + suspensionByProducer: { + who: authData.userId, + when: new Date(), + }, + }, }; const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, @@ -941,6 +949,11 @@ describe("activate agreement", () => { declaredAttributes: actualAgreement.declaredAttributes, verifiedAttributes: actualAgreement.verifiedAttributes, suspendedAt: undefined, + suspendedByProducer: false, + stamps: { + ...agreement.stamps, + suspensionByProducer: undefined, + }, }; expect(actualAgreement).toEqual(expectedAgreement); From dac5f198c2915e7d39a047cceb20b49f68eb1dd3 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Mon, 16 Dec 2024 15:34:39 +0100 Subject: [PATCH 088/126] Allow delegate attribute self verification (#1300) --- .../src/services/tenantService.ts | 51 ++++++++++--------- .../tenant-process/src/services/validators.ts | 24 ++++----- .../test/revokeVerifiedAttribute.test.ts | 2 +- .../test/verifyVerifiedAttribute.test.ts | 8 +-- 4 files changed, 40 insertions(+), 45 deletions(-) diff --git a/packages/tenant-process/src/services/tenantService.ts b/packages/tenant-process/src/services/tenantService.ts index c5cea6f443..64a9f2bdc8 100644 --- a/packages/tenant-process/src/services/tenantService.ts +++ b/packages/tenant-process/src/services/tenantService.ts @@ -79,6 +79,7 @@ import { verifiedAttributeSelfRevocationNotAllowed, agreementNotFound, notValidMailAddress, + verifiedAttributeSelfVerificationNotAllowed, } from "../model/domain/errors.js"; import { assertOrganizationIsInAttributeVerifiers, @@ -708,6 +709,7 @@ export function tenantServiceBuilder( agreementState.active, agreementState.suspended, ]; + if (!allowedStatuses.includes(agreement.state)) { throw error; } @@ -717,19 +719,21 @@ export function tenantServiceBuilder( agreement.eserviceId ); - const delegateProducerId = producerDelegation?.delegateId; - const producerDelegator = producerDelegation?.delegatorId; - await assertVerifiedAttributeOperationAllowed({ requesterId: organizationId, - delegateProducerId, - consumerId: tenantId, + producerDelegation, attributeId, agreement, readModelService, error, }); + const verifierId = agreement.producerId; + + if (verifierId === tenantId) { + throw verifiedAttributeSelfVerificationNotAllowed(); + } + const targetTenant = await retrieveTenant(tenantId, readModelService); const attribute = await retrieveAttribute(attributeId, readModelService); @@ -749,13 +753,13 @@ export function tenantServiceBuilder( ? reassignVerifiedAttribute( targetTenant.data.attributes, verifiedTenantAttribute, - producerDelegator ?? organizationId, + verifierId, producerDelegation?.id, expirationDate ) : assignVerifiedAttribute( targetTenant.data.attributes, - producerDelegator ?? organizationId, + verifierId, producerDelegation?.id, attributeId, expirationDate @@ -791,10 +795,6 @@ export function tenantServiceBuilder( `Revoking verified attribute ${attributeId} to tenant ${tenantId}` ); - if (authData.organizationId === tenantId) { - throw verifiedAttributeSelfRevocationNotAllowed(); - } - const targetTenant = await retrieveTenant(tenantId, readModelService); const agreement = await retrieveAgreement(agreementId, readModelService); @@ -805,6 +805,7 @@ export function tenantServiceBuilder( agreementState.active, agreementState.suspended, ]; + if (!allowedStatuses.includes(agreement.state)) { throw error; } @@ -814,19 +815,21 @@ export function tenantServiceBuilder( agreement.eserviceId ); - const delegateProducerId = producerDelegation?.delegateId; - const producerDelegator = producerDelegation?.delegatorId; - await assertVerifiedAttributeOperationAllowed({ requesterId: authData.organizationId, - delegateProducerId, - consumerId: tenantId, + producerDelegation, attributeId, agreement, readModelService, error, }); + const revokerId = agreement.producerId; + + if (revokerId === tenantId) { + throw verifiedAttributeSelfRevocationNotAllowed(); + } + const verifiedTenantAttribute = targetTenant.data.attributes.find( (attr): attr is VerifiedTenantAttribute => attr.type === tenantAttributeType.VERIFIED && attr.id === attributeId @@ -837,7 +840,7 @@ export function tenantServiceBuilder( } const verifier = verifiedTenantAttribute.verifiedBy.find( - (a) => a.id === authData.organizationId + (a) => a.id === revokerId ); if (!verifier) { @@ -845,15 +848,11 @@ export function tenantServiceBuilder( } const isInRevokedBy = verifiedTenantAttribute.revokedBy.some( - (a) => a.id === authData.organizationId + (a) => a.id === revokerId ); if (isInRevokedBy) { - throw attributeAlreadyRevoked( - tenantId, - authData.organizationId, - attributeId - ); + throw attributeAlreadyRevoked(tenantId, revokerId, attributeId); } const updatedTenant: Tenant = { @@ -864,13 +863,13 @@ export function tenantServiceBuilder( ? ({ ...verifiedTenantAttribute, verifiedBy: verifiedTenantAttribute.verifiedBy.filter( - (v) => v.id !== authData.organizationId + (v) => v.id !== revokerId ), revokedBy: [ ...verifiedTenantAttribute.revokedBy, { ...verifier, - id: producerDelegator ?? verifier.id, + id: revokerId, delegationId: producerDelegation?.id, revocationDate: new Date(), }, @@ -879,6 +878,7 @@ export function tenantServiceBuilder( : attr ), }; + await repository.createEvent( toCreateEventTenantVerifiedAttributeRevoked( targetTenant.metadata.version, @@ -887,6 +887,7 @@ export function tenantServiceBuilder( correlationId ) ); + return updatedTenant; }, diff --git a/packages/tenant-process/src/services/validators.ts b/packages/tenant-process/src/services/validators.ts index 11772337e6..a256b9110c 100644 --- a/packages/tenant-process/src/services/validators.ts +++ b/packages/tenant-process/src/services/validators.ts @@ -19,6 +19,7 @@ import { tenantKind, SCP, Agreement, + Delegation, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { @@ -28,7 +29,6 @@ import { selfcareIdConflict, expirationDateNotFoundInVerifier, tenantIsNotACertifier, - verifiedAttributeSelfVerificationNotAllowed, attributeNotFound, tenantAlreadyHasDelegatedProducerFeature, tenantHasNoDelegatedProducerFeature, @@ -49,23 +49,25 @@ export function assertVerifiedAttributeExistsInTenant( export async function assertVerifiedAttributeOperationAllowed({ requesterId, - delegateProducerId, - consumerId, + producerDelegation, attributeId, agreement, readModelService, error, }: { requesterId: TenantId; - delegateProducerId: TenantId | undefined; - consumerId: TenantId; + producerDelegation: Delegation | undefined; attributeId: AttributeId; agreement: Agreement; readModelService: ReadModelService; error: Error; }): Promise { - if ([requesterId, delegateProducerId].includes(consumerId)) { - throw verifiedAttributeSelfVerificationNotAllowed(); + if (producerDelegation && producerDelegation.delegateId !== requesterId) { + throw error; + } + + if (!producerDelegation && requesterId !== agreement.producerId) { + throw error; } const descriptorId = agreement.descriptorId; @@ -94,14 +96,6 @@ export async function assertVerifiedAttributeOperationAllowed({ if (!attributeIds.has(attributeId)) { throw error; } - - if (delegateProducerId && delegateProducerId !== requesterId) { - throw error; - } - - if (!delegateProducerId && requesterId !== agreement.producerId) { - throw error; - } } export function assertOrganizationVerifierExist( diff --git a/packages/tenant-process/test/revokeVerifiedAttribute.test.ts b/packages/tenant-process/test/revokeVerifiedAttribute.test.ts index 144ac351b3..8967fb4428 100644 --- a/packages/tenant-process/test/revokeVerifiedAttribute.test.ts +++ b/packages/tenant-process/test/revokeVerifiedAttribute.test.ts @@ -172,7 +172,7 @@ describe("revokeVerifiedAttribute", async () => { verifiedBy: [], revokedBy: [ { - id: hasDelegation ? delegation.delegatorId : revokerTenant.id, + id: revokerTenant.id, delegationId: hasDelegation ? delegation.id : undefined, verificationDate: mockVerifiedBy.verificationDate, revocationDate: new Date(), diff --git a/packages/tenant-process/test/verifyVerifiedAttribute.test.ts b/packages/tenant-process/test/verifyVerifiedAttribute.test.ts index 73156b35ba..217b6a2477 100644 --- a/packages/tenant-process/test/verifyVerifiedAttribute.test.ts +++ b/packages/tenant-process/test/verifyVerifiedAttribute.test.ts @@ -159,7 +159,7 @@ describe("verifyVerifiedAttribute", async () => { assignmentTimestamp: new Date(), verifiedBy: [ { - id: hasDelegation ? delegation.delegatorId : requesterTenant.id, + id: eService1.producerId, delegationId: hasDelegation ? delegation.id : undefined, verificationDate: new Date(), expirationDate: undefined, @@ -253,7 +253,7 @@ describe("verifyVerifiedAttribute", async () => { { ...mockVerifiedBy }, { ...mockVerifiedBy, - id: hasDelegation ? delegation.delegatorId : requesterTenant.id, + id: eService1.producerId, delegationId: hasDelegation ? delegation.id : undefined, verificationDate: new Date(), }, @@ -364,10 +364,10 @@ describe("verifyVerifiedAttribute", async () => { expect( tenantService.verifyVerifiedAttribute( { - tenantId: targetTenant.id, + tenantId: agreementEservice1.producerId, attributeId: tenantAttributeSeedId, agreementId: agreementEservice1.id, - organizationId: targetTenant.id, + organizationId: agreementEservice1.producerId, correlationId: generateId(), }, genericLogger From 76592b58e258903ede7d1dbd84adb4418121a427 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 18 Dec 2024 09:52:38 +0100 Subject: [PATCH 089/126] IMN-787: Fix config in token-details-persister session timeout (#1308) --- .../src/config/consumerServiceConfig.ts | 27 ++++++++++--------- packages/kafka-iam-auth/src/index.ts | 18 +++++-------- .../src/config/config.ts | 8 ++++++ packages/token-details-persister/src/index.ts | 15 ++++++++--- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/packages/commons/src/config/consumerServiceConfig.ts b/packages/commons/src/config/consumerServiceConfig.ts index 3c151e2f1d..7ac157828a 100644 --- a/packages/commons/src/config/consumerServiceConfig.ts +++ b/packages/commons/src/config/consumerServiceConfig.ts @@ -20,19 +20,22 @@ export const KafkaConsumerConfig = KafkaConfig.and( ); export type KafkaConsumerConfig = z.infer; -export const KafkaBatchConsumerConfig = KafkaConsumerConfig.and( - z - .object({ - AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES: z.coerce.number(), - MESSAGES_TO_READ_PER_BATCH: z.coerce.number(), - MAX_WAIT_KAFKA_BATCH_MILLIS: z.coerce.number(), - }) - .transform((c) => ({ - averageKafkaMessageSizeInBytes: c.AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES, - messagesToReadPerBatch: c.MESSAGES_TO_READ_PER_BATCH, +export const KafkaBatchConsumerConfig = z + .object({ + AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES: z.coerce.number(), + MESSAGES_TO_READ_PER_BATCH: z.coerce.number(), + MAX_WAIT_KAFKA_BATCH_MILLIS: z.coerce.number(), + }) + .transform((c) => { + const minBytes = + c.AVERAGE_KAFKA_MESSAGE_SIZE_IN_BYTES * c.MESSAGES_TO_READ_PER_BATCH; + return { + minBytes, maxWaitKafkaBatchMillis: c.MAX_WAIT_KAFKA_BATCH_MILLIS, - })) -); + sessionTimeoutMillis: Math.round(c.MAX_WAIT_KAFKA_BATCH_MILLIS * 1.5), + maxBytes: Math.round(minBytes * 1.25), + }; + }); export type KafkaBatchConsumerConfig = z.infer; export const ReadModelWriterConfig = KafkaConsumerConfig.and(ReadModelDbConfig); diff --git a/packages/kafka-iam-auth/src/index.ts b/packages/kafka-iam-auth/src/index.ts index 3324468bdd..6ffb6d763f 100644 --- a/packages/kafka-iam-auth/src/index.ts +++ b/packages/kafka-iam-auth/src/index.ts @@ -237,12 +237,12 @@ const initCustomConsumer = async ({ config, topics, consumerRunConfig, - batchConfig, + batchConsumerConfig, }: { config: KafkaConsumerConfig; topics: string[]; consumerRunConfig: (consumer: Consumer) => ConsumerRunConfig; - batchConfig?: { minBytes: number; maxWaitTimeInMs: number }; + batchConsumerConfig?: KafkaBatchConsumerConfig; }): Promise => { genericLogger.debug( `Consumer connecting to topics ${JSON.stringify(topics)}` @@ -261,8 +261,7 @@ const initCustomConsumer = async ({ return Promise.resolve(false); }, }, - ...batchConfig, - maxBytes: batchConfig ? batchConfig.minBytes * 1.25 : undefined, // TODO double-check + ...batchConsumerConfig, }); if (config.resetConsumerOffsets) { @@ -385,7 +384,8 @@ export const runConsumer = async ( }; export const runBatchConsumer = async ( - config: KafkaBatchConsumerConfig, + baseConsumerConfig: KafkaConsumerConfig, + batchConsumerConfig: KafkaBatchConsumerConfig, topics: string[], consumerHandlerBatch: (messagePayload: EachBatchPayload) => Promise ): Promise => { @@ -405,14 +405,10 @@ export const runBatchConsumer = async ( }, }); await initCustomConsumer({ - config, + config: baseConsumerConfig, topics, consumerRunConfig, - batchConfig: { - minBytes: - config.averageKafkaMessageSizeInBytes * config.messagesToReadPerBatch, - maxWaitTimeInMs: config.maxWaitKafkaBatchMillis, - }, + batchConsumerConfig, }); } catch (e) { genericLogger.error( diff --git a/packages/token-details-persister/src/config/config.ts b/packages/token-details-persister/src/config/config.ts index 914e1ceedc..c8b5283b6d 100644 --- a/packages/token-details-persister/src/config/config.ts +++ b/packages/token-details-persister/src/config/config.ts @@ -1,12 +1,14 @@ import { FileManagerConfig, KafkaBatchConsumerConfig, + KafkaConsumerConfig, LoggerConfig, S3Config, } from "pagopa-interop-commons"; import { z } from "zod"; export const TokenDetailsPersisterConfig = FileManagerConfig.and(S3Config) + .and(KafkaConsumerConfig) .and(KafkaBatchConsumerConfig) .and(LoggerConfig) .and( @@ -25,3 +27,9 @@ export type TokenDetailsPersisterConfig = z.infer< export const config: TokenDetailsPersisterConfig = TokenDetailsPersisterConfig.parse(process.env); + +export const baseConsumerConfig: KafkaConsumerConfig = + KafkaConsumerConfig.parse(process.env); + +export const batchConsumerConfig: KafkaBatchConsumerConfig = + KafkaBatchConsumerConfig.parse(process.env); diff --git a/packages/token-details-persister/src/index.ts b/packages/token-details-persister/src/index.ts index 22f77c13c7..552e17b18b 100644 --- a/packages/token-details-persister/src/index.ts +++ b/packages/token-details-persister/src/index.ts @@ -1,7 +1,11 @@ import { EachBatchPayload } from "kafkajs"; import { initFileManager, logger } from "pagopa-interop-commons"; import { runBatchConsumer } from "kafka-iam-auth"; -import { config } from "./config/config.js"; +import { + baseConsumerConfig, + config, + batchConsumerConfig, +} from "./config/config.js"; import { handleMessages } from "./consumerService.js"; const fileManager = initFileManager(config); @@ -13,10 +17,15 @@ async function processMessage({ batch }: EachBatchPayload): Promise { await handleMessages(batch.messages, fileManager, loggerInstance); loggerInstance.info( - `Auditing message was handled. Partition number: ${ + `Handling audit messages. Partition number: ${ batch.partition }. Offset: ${batch.firstOffset()} -> ${batch.lastOffset()}` ); } -await runBatchConsumer(config, [config.tokenAuditingTopic], processMessage); +await runBatchConsumer( + baseConsumerConfig, + batchConsumerConfig, + [config.tokenAuditingTopic], + processMessage +); From ee9d0e3c5a35bbca8139bc2f78ae31a6ba676ab3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 18 Dec 2024 10:53:29 +0100 Subject: [PATCH 090/126] IMN-787 Fix token details persister (#1280) Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- docker/docker-compose.yml | 1 + packages/commons/src/file-manager/fileManager.ts | 6 ++++++ packages/token-details-persister/.env | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index c4f5c4a841..6934d5db25 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -179,6 +179,7 @@ services: mc mb minio/interop-datalake-bucket || true; mc mb minio/interop-application-import-export-local || true; mc mb minio/interop-data-lake-interfaces-exports-local-es1 || true; + mc mb --with-lock minio/interop-generated-jwt-audit || true; mc cp --recursive data/ minio/; " volumes: diff --git a/packages/commons/src/file-manager/fileManager.ts b/packages/commons/src/file-manager/fileManager.ts index ce986cc900..1a1001605e 100644 --- a/packages/commons/src/file-manager/fileManager.ts +++ b/packages/commons/src/file-manager/fileManager.ts @@ -1,4 +1,5 @@ /* eslint-disable max-params */ +import crypto from "crypto"; import { CopyObjectCommand, DeleteObjectCommand, @@ -91,6 +92,11 @@ export function initFileManager( Bucket: bucket, Key: key, Body: fileContent, + ChecksumSHA256: crypto + .createHash("sha256") + .update(fileContent) + .digest("base64") + .toString(), }) ); return key; diff --git a/packages/token-details-persister/.env b/packages/token-details-persister/.env index 0a4a79e00d..fffd3219de 100644 --- a/packages/token-details-persister/.env +++ b/packages/token-details-persister/.env @@ -5,7 +5,7 @@ KAFKA_GROUP_ID="TODO" KAFKA_BROKERS="localhost:9092" KAFKA_DISABLE_AWS_IAM_AUTH="true" TOKEN_AUDITING_TOPIC="authorization-server.generated-jwt" -S3_BUCKET=interop-local-bucket +S3_BUCKET=interop-generated-jwt-audit S3_CUSTOM_SERVER=true S3_SERVER_HOST=http://localhost S3_SERVER_PORT=9000 From 14138ae834cf745d21c358a0e2f7bd4939a1333d Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 20 Dec 2024 09:28:20 +0100 Subject: [PATCH 091/126] PIN-5875 - Fix Delegations pagination (#1322) --- .../src/routers/DelegationRouter.ts | 6 ++---- .../src/services/delegationService.ts | 3 ++- .../src/services/readModelService.ts | 11 ++++++++-- .../test/getDelegations.test.ts | 20 +++++++++---------- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/packages/delegation-process/src/routers/DelegationRouter.ts b/packages/delegation-process/src/routers/DelegationRouter.ts index 9c136bc539..c715c26ac1 100644 --- a/packages/delegation-process/src/routers/DelegationRouter.ts +++ b/packages/delegation-process/src/routers/DelegationRouter.ts @@ -82,10 +82,8 @@ const delegationRouter = ( return res.status(200).send( delegationApi.Delegations.parse({ - results: delegations.map((delegation) => - delegationToApiDelegation(delegation) - ), - totalCount: delegations.length, + results: delegations.results.map(delegationToApiDelegation), + totalCount: delegations.totalCount, }) ); } catch (error) { diff --git a/packages/delegation-process/src/services/delegationService.ts b/packages/delegation-process/src/services/delegationService.ts index ecd9db121a..dfec23af86 100644 --- a/packages/delegation-process/src/services/delegationService.ts +++ b/packages/delegation-process/src/services/delegationService.ts @@ -6,6 +6,7 @@ import { DelegationKind, DelegationState, EServiceId, + ListResult, TenantId, WithMetadata, } from "pagopa-interop-models"; @@ -62,7 +63,7 @@ export function delegationServiceBuilder(readModelService: ReadModelService) { limit: number; }, logger: Logger - ): Promise { + ): Promise> { logger.info( `Retrieving delegations with filters: delegateIds=${delegateIds}, delegatorIds=${delegatorIds}, delegationStates=${delegationStates}, eserviceIds=${eserviceIds}, kind=${kind}, offset=${offset}, limit=${limit}` ); diff --git a/packages/delegation-process/src/services/readModelService.ts b/packages/delegation-process/src/services/readModelService.ts index bacc278f27..a2513cebe0 100644 --- a/packages/delegation-process/src/services/readModelService.ts +++ b/packages/delegation-process/src/services/readModelService.ts @@ -13,6 +13,7 @@ import { EServiceId, EServiceReadModel, genericInternalError, + ListResult, Tenant, TenantId, WithMetadata, @@ -196,7 +197,7 @@ export function readModelServiceBuilder( kind: DelegationKind | undefined; offset: number; limit: number; - }): Promise { + }): Promise> { const aggregationPipeline = [ { $match: { @@ -243,7 +244,13 @@ export function readModelServiceBuilder( ); } - return result.data; + return { + results: result.data, + totalCount: await ReadModelRepository.getTotalCount( + delegations, + aggregationPipeline + ), + }; }, }; } diff --git a/packages/delegation-process/test/getDelegations.test.ts b/packages/delegation-process/test/getDelegations.test.ts index 2d22269099..60050d44ad 100644 --- a/packages/delegation-process/test/getDelegations.test.ts +++ b/packages/delegation-process/test/getDelegations.test.ts @@ -26,7 +26,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res1).toEqual([delegation1]); + expect(res1.results).toEqual([delegation1]); const res2 = await delegationService.getDelegations( { @@ -40,7 +40,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res2).toEqual([delegation2]); + expect(res2.results).toEqual([delegation2]); const res3 = await delegationService.getDelegations( { @@ -54,7 +54,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res3).toEqual([]); + expect(res3.results).toEqual([]); const res4 = await delegationService.getDelegations( { @@ -68,7 +68,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res4).toEqual([delegation1]); + expect(res4.results).toEqual([delegation1]); const res5 = await delegationService.getDelegations( { @@ -82,7 +82,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res5).toEqual([delegation1]); + expect(res5.results).toEqual([delegation1]); }); it("should get consumer's delegations", async () => { @@ -105,7 +105,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res1).toEqual([delegation1]); + expect(res1.results).toEqual([delegation1]); const res2 = await delegationService.getDelegations( { @@ -119,7 +119,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res2).toEqual([delegation2]); + expect(res2.results).toEqual([delegation2]); const res3 = await delegationService.getDelegations( { @@ -133,7 +133,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res3).toEqual([]); + expect(res3.results).toEqual([]); const res4 = await delegationService.getDelegations( { @@ -147,7 +147,7 @@ describe("get delegations", () => { }, genericLogger ); - expect(res4).toEqual([delegation1]); + expect(res4.results).toEqual([delegation1]); const res5 = await delegationService.getDelegations( { @@ -161,6 +161,6 @@ describe("get delegations", () => { }, genericLogger ); - expect(res5).toEqual([delegation1]); + expect(res5.results).toEqual([delegation1]); }); }); From dad396086c445561c964fe8e0bfbecc3ed79c343 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Fri, 20 Dec 2024 11:39:14 +0100 Subject: [PATCH 092/126] Added delegation outbound writer (#1324) --- .../agreement-outbound-writer/package.json | 2 +- packages/catalog-outbound-writer/package.json | 2 +- packages/delegation-outbound-writer/.env | 14 +++ .../delegation-outbound-writer/Dockerfile | 45 ++++++++++ .../delegation-outbound-writer/package.json | 41 +++++++++ .../src/config/config.ts | 27 ++++++ .../src/converters/toOutboundEventV2.ts | 85 +++++++++++++++++++ .../delegation-outbound-writer/src/index.ts | 56 ++++++++++++ .../tsconfig.check.json | 7 ++ .../delegation-outbound-writer/tsconfig.json | 9 ++ packages/purpose-outbound-writer/package.json | 2 +- packages/tenant-outbound-writer/package.json | 2 +- pnpm-lock.yaml | 80 ++++++++++++++--- 13 files changed, 357 insertions(+), 15 deletions(-) create mode 100644 packages/delegation-outbound-writer/.env create mode 100644 packages/delegation-outbound-writer/Dockerfile create mode 100644 packages/delegation-outbound-writer/package.json create mode 100644 packages/delegation-outbound-writer/src/config/config.ts create mode 100644 packages/delegation-outbound-writer/src/converters/toOutboundEventV2.ts create mode 100644 packages/delegation-outbound-writer/src/index.ts create mode 100644 packages/delegation-outbound-writer/tsconfig.check.json create mode 100644 packages/delegation-outbound-writer/tsconfig.json diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index ff778c2c94..d34fc08e0e 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11e", + "@pagopa/interop-outbound-models": "1.0.11f", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/catalog-outbound-writer/package.json b/packages/catalog-outbound-writer/package.json index 56b8fcd01a..677360135e 100644 --- a/packages/catalog-outbound-writer/package.json +++ b/packages/catalog-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11e", + "@pagopa/interop-outbound-models": "1.0.11f", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/delegation-outbound-writer/.env b/packages/delegation-outbound-writer/.env new file mode 100644 index 0000000000..3c71cf33c7 --- /dev/null +++ b/packages/delegation-outbound-writer/.env @@ -0,0 +1,14 @@ +LOG_LEVEL=info + +KAFKA_CLIENT_ID="delegation" +KAFKA_GROUP_ID="delegation-group-local" +KAFKA_BROKERS="localhost:9092" +KAFKA_DISABLE_AWS_IAM_AUTH="true" +DELEGATION_TOPIC="event-store.delegation.events" + +PRODUCER_KAFKA_CLIENT_ID="delegation" +PRODUCER_KAFKA_BROKERS="localhost:9092" +PRODUCER_KAFKA_DISABLE_AWS_IAM_AUTH="true" +DELEGATION_OUTBOUND_TOPIC="event-store.delegation-outbound.events" + + diff --git a/packages/delegation-outbound-writer/Dockerfile b/packages/delegation-outbound-writer/Dockerfile new file mode 100644 index 0000000000..f502391e7b --- /dev/null +++ b/packages/delegation-outbound-writer/Dockerfile @@ -0,0 +1,45 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/delegation-outbound-writer/package.json /app/packages/delegation-outbound-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/delegation-outbound-writer /app/packages/delegation-outbound-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/delegation-outbound-writer/node_modules \ + package*.json packages/delegation-outbound-writer/package*.json \ + packages/commons/ \ + packages/models/ \ + packages/kafka-iam-auth/ \ + packages/delegation-outbound-writer/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/delegation-outbound-writer +EXPOSE 3000 + +CMD [ "node", "." ] diff --git a/packages/delegation-outbound-writer/package.json b/packages/delegation-outbound-writer/package.json new file mode 100644 index 0000000000..3e13c94771 --- /dev/null +++ b/packages/delegation-outbound-writer/package.json @@ -0,0 +1,41 @@ +{ + "name": "pagopa-interop-delegation-outbound-writer", + "private": true, + "version": "1.0.0", + "main": "dist", + "type": "module", + "scripts": { + "lint": "eslint . --ext .ts,.tsx", + "lint:autofix": "eslint . --ext .ts,.tsx --fix", + "format:check": "prettier --check src", + "format:write": "prettier --write src", + "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@anatine/zod-mock": "3.13.4", + "@faker-js/faker": "8.4.1", + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "prettier": "2.8.8", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "@pagopa/interop-outbound-models": "1.0.11f", + "@protobuf-ts/runtime": "2.9.4", + "connection-string": "4.4.0", + "dotenv-flow": "4.1.0", + "kafka-iam-auth": "workspace:*", + "kafkajs": "2.2.4", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "ts-pattern": "5.2.0", + "zod": "3.23.8" + } +} diff --git a/packages/delegation-outbound-writer/src/config/config.ts b/packages/delegation-outbound-writer/src/config/config.ts new file mode 100644 index 0000000000..9e2209e9bd --- /dev/null +++ b/packages/delegation-outbound-writer/src/config/config.ts @@ -0,0 +1,27 @@ +import { + DelegationTopicConfig, + KafkaConsumerConfig, + KafkaProducerConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const DelegationOutboundWriterConfig = KafkaConsumerConfig.and( + KafkaProducerConfig +) + .and(DelegationTopicConfig) + .and( + z.object({ + DELEGATION_OUTBOUND_TOPIC: z.string(), + }) + ) + .transform((c) => ({ + ...c, + delegationOutboundTopic: c.DELEGATION_OUTBOUND_TOPIC, + })); + +export type DelegationOutboundWriterConfig = z.infer< + typeof DelegationOutboundWriterConfig +>; + +export const config: DelegationOutboundWriterConfig = + DelegationOutboundWriterConfig.parse(process.env); diff --git a/packages/delegation-outbound-writer/src/converters/toOutboundEventV2.ts b/packages/delegation-outbound-writer/src/converters/toOutboundEventV2.ts new file mode 100644 index 0000000000..5003a04a3b --- /dev/null +++ b/packages/delegation-outbound-writer/src/converters/toOutboundEventV2.ts @@ -0,0 +1,85 @@ +import { + DelegationEventEnvelopeV2, + DelegationV2, + DelegationContractDocumentV2, + DelegationStampV2, + DelegationStampsV2, +} from "pagopa-interop-models"; +import { + DelegationEvent as OutboundDelegationEvent, + DelegationV2 as OutboundDelegationV2, + DelegationContractDocumentV2 as OutboundDelegationContractDocumentV2, + DelegationStampV2 as OutboundDelegationStampV2, + DelegationStampsV2 as OutboundDelegationStampsV2, +} from "@pagopa/interop-outbound-models"; +import { match } from "ts-pattern"; +import { Exact } from "pagopa-interop-commons"; + +function toOuboundDelegationContractDocumentV2( + document: DelegationContractDocumentV2 +): Exact { + return { + ...document, + path: undefined, + }; +} + +function toOutboundStampV2( + stamp: DelegationStampV2 +): Exact { + return { + ...stamp, + who: undefined, + }; +} + +function toOutboundStampsV2( + stamp: DelegationStampsV2 +): Exact { + return { + activation: stamp.activation && toOutboundStampV2(stamp.activation), + rejection: stamp.rejection && toOutboundStampV2(stamp.rejection), + revocation: stamp.revocation && toOutboundStampV2(stamp.revocation), + submission: stamp.submission && toOutboundStampV2(stamp.submission), + }; +} + +function toOutboundDelegationV2( + delegation: DelegationV2 +): Exact { + return { + ...delegation, + activationContract: + delegation.activationContract && + toOuboundDelegationContractDocumentV2(delegation.activationContract), + revocationContract: + delegation.revocationContract && + toOuboundDelegationContractDocumentV2(delegation.revocationContract), + stamps: delegation.stamps && toOutboundStampsV2(delegation.stamps), + }; +} + +export function toOutboundEventV2( + message: DelegationEventEnvelopeV2 +): OutboundDelegationEvent | undefined { + return match(message) + .returnType() + .with( + { type: "ProducerDelegationSubmitted" }, + { type: "ProducerDelegationApproved" }, + { type: "ProducerDelegationRejected" }, + { type: "ProducerDelegationRevoked" }, + (msg) => ({ + event_version: msg.event_version, + type: msg.type, + version: msg.version, + data: { + delegation: + msg.data.delegation && toOutboundDelegationV2(msg.data.delegation), + }, + stream_id: msg.stream_id, + timestamp: new Date(), + }) + ) + .exhaustive(); +} diff --git a/packages/delegation-outbound-writer/src/index.ts b/packages/delegation-outbound-writer/src/index.ts new file mode 100644 index 0000000000..162c697528 --- /dev/null +++ b/packages/delegation-outbound-writer/src/index.ts @@ -0,0 +1,56 @@ +import { EachMessagePayload } from "kafkajs"; +import { decodeKafkaMessage, logger } from "pagopa-interop-commons"; +import { initProducer, runConsumer } from "kafka-iam-auth"; +import { match } from "ts-pattern"; +import { + encodeOutboundDelegationEvent, + DelegationEvent as DelegationOutboundEvent, +} from "@pagopa/interop-outbound-models"; +import { + CorrelationId, + DelegationEvent, + generateId, + unsafeBrandId, +} from "pagopa-interop-models"; +import { config } from "./config/config.js"; +import { toOutboundEventV2 } from "./converters/toOutboundEventV2.js"; + +const producer = await initProducer(config, config.delegationOutboundTopic); + +async function processMessage({ + message, + partition, +}: EachMessagePayload): Promise { + const msg = decodeKafkaMessage(message, DelegationEvent); + + const loggerInstance = logger({ + serviceName: "delegation-outbound-writer", + eventType: msg.type, + eventVersion: msg.event_version, + streamId: msg.stream_id, + correlationId: msg.correlation_id + ? unsafeBrandId(msg.correlation_id) + : generateId(), + }); + + const outboundEvent: DelegationOutboundEvent | undefined = match(msg) + .with({ event_version: 2 }, (msg) => toOutboundEventV2(msg)) + .exhaustive(); + + if (outboundEvent) { + await producer.send({ + messages: [ + { + key: outboundEvent.stream_id, + value: encodeOutboundDelegationEvent(outboundEvent), + }, + ], + }); + } + + loggerInstance.info( + `Outbound event sent! Partition number: ${partition}. Offset: ${message.offset}` + ); +} + +await runConsumer(config, [config.delegationTopic], processMessage); diff --git a/packages/delegation-outbound-writer/tsconfig.check.json b/packages/delegation-outbound-writer/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/delegation-outbound-writer/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/delegation-outbound-writer/tsconfig.json b/packages/delegation-outbound-writer/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/delegation-outbound-writer/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/purpose-outbound-writer/package.json b/packages/purpose-outbound-writer/package.json index ac1de66964..bd7b602ed6 100644 --- a/packages/purpose-outbound-writer/package.json +++ b/packages/purpose-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11e", + "@pagopa/interop-outbound-models": "1.0.11f", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/packages/tenant-outbound-writer/package.json b/packages/tenant-outbound-writer/package.json index 2ff794e76a..e8cae97979 100644 --- a/packages/tenant-outbound-writer/package.json +++ b/packages/tenant-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.0.11e", + "@pagopa/interop-outbound-models": "1.0.11f", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1069e32b6d..743b268d8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11e - version: 1.0.11-e + specifier: 1.0.11f + version: 1.0.11-f '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1063,8 +1063,8 @@ importers: packages/catalog-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11e - version: 1.0.11-e + specifier: 1.0.11f + version: 1.0.11-f '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -1791,6 +1791,64 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/delegation-outbound-writer: + dependencies: + '@pagopa/interop-outbound-models': + specifier: 1.0.11f + version: 1.0.11-f + '@protobuf-ts/runtime': + specifier: 2.9.4 + version: 2.9.4 + connection-string: + specifier: 4.4.0 + version: 4.4.0 + dotenv-flow: + specifier: 4.1.0 + version: 4.1.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.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@anatine/zod-mock': + specifier: 3.13.4 + version: 3.13.4(@faker-js/faker@8.4.1)(zod@3.23.8) + '@faker-js/faker': + specifier: 8.4.1 + version: 8.4.1 + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + prettier: + specifier: 2.8.8 + version: 2.8.8 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages/delegation-process: dependencies: '@zodios/core': @@ -2595,8 +2653,8 @@ importers: packages/purpose-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11e - version: 1.0.11-e + specifier: 1.0.11f + version: 1.0.11-f '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -2900,8 +2958,8 @@ importers: packages/tenant-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.0.11e - version: 1.0.11-e + specifier: 1.0.11f + version: 1.0.11-f '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -3963,8 +4021,8 @@ packages: '@pagopa/eslint-config@3.0.0': resolution: {integrity: sha512-eYIPdiuYRbRPR5k0OuteRNqYb0Z2nfJ/lZohejB7ylfBeSDWwkaV8Z19AXP4RymE6oEesyPDZ6i0yNaE9tQrHw==} - '@pagopa/interop-outbound-models@1.0.11-e': - resolution: {integrity: sha512-SB3GOaHYwTuGFw2Jr6mlFnekQNZuLy1E63UuIhMk0KXFKTTxOnBT6fdfyQj4WrAnVmzbGAvVQ/WmMz9I00n4UQ==} + '@pagopa/interop-outbound-models@1.0.11-f': + resolution: {integrity: sha512-zgDL6/UyWhm15LTm/n3dF8y74JgccQZj8uaQXIdONxdWEbXIBtgr8Z9Rtk1iAVUYeB6ZyF+53z01oquGfYI/dw==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -9269,7 +9327,7 @@ snapshots: - tsutils - typescript - '@pagopa/interop-outbound-models@1.0.11-e': + '@pagopa/interop-outbound-models@1.0.11-f': dependencies: '@protobuf-ts/runtime': 2.9.4 ts-pattern: 5.2.0 From cc7232c7f3b7a670d2fc0248e5585e9532a92085 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:45:38 +0100 Subject: [PATCH 093/126] IMN-791 - Fix agreement-platformstate-writer (#1331) Co-authored-by: Roberto Taglioni --- .../src/consumerServiceV1.ts | 122 +++++------------- .../consumerServiceV1.integration.test.ts | 59 +++------ 2 files changed, 54 insertions(+), 127 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index e194dd5076..6cd0f00fb3 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -33,24 +33,19 @@ export async function handleMessageV1( logger: Logger ): Promise { await match(message) - .with({ type: "AgreementActivated" }, async (msg) => { - const agreement = parseAgreement(msg.data.agreement); - await handleFirstActivation( - agreement, - dynamoDBClient, - msg.version, - logger - ); - }) - .with({ type: "AgreementSuspended" }, async (msg) => { - const agreement = parseAgreement(msg.data.agreement); - await handleActivationOrSuspension( - agreement, - dynamoDBClient, - msg.version, - logger - ); - }) + .with( + { type: "AgreementActivated" }, + { type: "AgreementSuspended" }, + async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + await handleActivationOrSuspension( + agreement, + dynamoDBClient, + msg.version, + logger + ); + } + ) .with({ type: "AgreementUpdated" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); @@ -119,7 +114,7 @@ const parseAgreement = (agreementV1: AgreementV1 | undefined): Agreement => { return fromAgreementV1(agreementV1); }; -const handleFirstActivation = async ( +const handleActivationOrSuspension = async ( agreement: Agreement, dynamoDBClient: DynamoDBClient, incomingVersion: number, @@ -149,15 +144,22 @@ const handleFirstActivation = async ( ); } } else { + const agreementTimestamp = agreement.stamps.activation + ? agreement.stamps.activation.when.toISOString() + : agreement.createdAt.toISOString(); + if (agreement.stamps.activation === undefined) { + logger.warn( + `Missing agreement activation stamp for agreement with id ${agreement.id}. Using createdAt as fallback.` + ); + } + const agreementEntry: PlatformStatesAgreementEntry = { PK: primaryKey, state: agreementStateToItemState(agreement.state), version: incomingVersion, updatedAt: new Date().toISOString(), GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - agreement.stamps.activation!.when.toISOString(), + GSISK_agreementTimestamp: agreementTimestamp, agreementDescriptorId: agreement.descriptorId, }; @@ -175,7 +177,6 @@ const handleFirstActivation = async ( eserviceId: agreement.eserviceId, descriptorId: agreement.descriptorId, }); - const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ @@ -196,68 +197,6 @@ const handleFirstActivation = async ( } }; -const handleActivationOrSuspension = async ( - agreement: Agreement, - dynamoDBClient: DynamoDBClient, - incomingVersion: number, - logger: Logger -): Promise => { - const primaryKey = makePlatformStatesAgreementPK(agreement.id); - - const existingAgreementEntry = await readAgreementEntry( - primaryKey, - dynamoDBClient - ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }); - - if (existingAgreementEntry) { - if (existingAgreementEntry.version > incomingVersion) { - // Stops processing if the message is older than the agreement entry - return Promise.resolve(); - } else { - await updateAgreementStateInPlatformStatesEntry( - dynamoDBClient, - primaryKey, - agreementStateToItemState(agreement.state), - incomingVersion - ); - } - } - - const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); - - const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - dynamoDBClient - ) - ) { - // token-generation-states - await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ - GSIPK_consumerId_eserviceId, - agreementId: agreement.id, - agreementState: agreement.state, - dynamoDBClient, - GSIPK_eserviceId_descriptorId, - catalogEntry, - logger, - }); - } -}; - const handleArchiving = async ( agreement: Agreement, dynamoDBClient: DynamoDBClient @@ -319,15 +258,22 @@ const handleUpgrade = async ( ); } } else { + const agreementTimestamp = agreement.stamps.activation + ? agreement.stamps.activation.when.toISOString() + : agreement.createdAt.toISOString(); + if (agreement.stamps.activation === undefined) { + logger.warn( + `Missing agreement activation stamp for agreement with id ${agreement.id}. Using createdAt as fallback.` + ); + } + const newAgreementEntry: PlatformStatesAgreementEntry = { PK: primaryKey, state: agreementStateToItemState(agreement.state), version: msgVersion, updatedAt: new Date().toISOString(), GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - agreement.stamps.activation!.when.toISOString(), + GSISK_agreementTimestamp: agreementTimestamp, agreementDescriptorId: agreement.descriptorId, }; diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 6eb6bdeb55..fa2616f1bc 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -1295,42 +1295,6 @@ describe("integration tests V1 events", async () => { }); describe("AgreementUpdated (suspended by producer)", async () => { - it("should do no operation if the entry doesn't exist", async () => { - const agreement: Agreement = { - ...getMockAgreement(), - state: agreementState.suspended, - stamps: { - activation: { - when: new Date(), - who: generateId(), - }, - }, - }; - const payload: AgreementUpdatedV1 = { - agreement: toAgreementV1(agreement), - }; - const message: AgreementEventEnvelope = { - sequence_num: 1, - stream_id: agreement.id, - version: 1, - type: "AgreementUpdated", - event_version: 1, - data: payload, - log_date: new Date(), - }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - - await handleMessageV1(message, dynamoDBClient, genericLogger); - - const retrievedAgreementEntry = await readAgreementEntry( - agreementEntryPrimaryKey, - dynamoDBClient - ); - - expect(retrievedAgreementEntry).toBeUndefined(); - }); it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1633,8 +1597,8 @@ describe("integration tests V1 events", async () => { }); }); - describe("AgreementUpdated (unsuspended by producer)", async () => { - it("should do no operation if the entry doesn't exist", async () => { + describe("AgreementUpdated (first activation)", () => { + it("should add the entry if it doesn't exist", async () => { const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, @@ -1668,8 +1632,25 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - expect(retrievedAgreementEntry).toBeUndefined(); + const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation!.when.toISOString(), + version: 1, + updatedAt: new Date().toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + expect(retrievedAgreementEntry).toEqual(expectedAgreementStateEntry); }); + }); + + describe("AgreementUpdated (unsuspended by producer)", async () => { it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); From d9035aa416ee4c201c48736b0347003bce41a7e1 Mon Sep 17 00:00:00 2001 From: Carmine Porricelli Date: Tue, 7 Jan 2025 14:03:37 +0100 Subject: [PATCH 094/126] Added `producerId` in `PublicEService` dtd data export (#1327) --- packages/dtd-catalog-exporter/src/models/converters.ts | 1 + packages/dtd-catalog-exporter/src/models/models.ts | 1 + .../dtd-catalog-exporter/test/exportDtdPublicCatalog.test.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/packages/dtd-catalog-exporter/src/models/converters.ts b/packages/dtd-catalog-exporter/src/models/converters.ts index 43840b205c..09a6df730f 100644 --- a/packages/dtd-catalog-exporter/src/models/converters.ts +++ b/packages/dtd-catalog-exporter/src/models/converters.ts @@ -32,6 +32,7 @@ export function toPublicEService( name: eservice.name, description: eservice.description, technology: eservice.technology.toUpperCase() as "REST" | "SOAP", + producerId: producer.id, producerName: producer.name, attributes: toPublicAttributes(activeDescriptor.attributes, attributesMap), activeDescriptor: { diff --git a/packages/dtd-catalog-exporter/src/models/models.ts b/packages/dtd-catalog-exporter/src/models/models.ts index befbaf9201..1028d9e2ce 100644 --- a/packages/dtd-catalog-exporter/src/models/models.ts +++ b/packages/dtd-catalog-exporter/src/models/models.ts @@ -50,6 +50,7 @@ export const PublicEService = z.object({ activeDescriptor: PublicEServiceDescriptor, technology: z.enum(["REST", "SOAP"]), producerName: z.string(), + producerId: z.string(), id: z.string(), name: z.string(), description: z.string(), diff --git a/packages/dtd-catalog-exporter/test/exportDtdPublicCatalog.test.ts b/packages/dtd-catalog-exporter/test/exportDtdPublicCatalog.test.ts index 443a5f7d39..6f93b3ce3e 100644 --- a/packages/dtd-catalog-exporter/test/exportDtdPublicCatalog.test.ts +++ b/packages/dtd-catalog-exporter/test/exportDtdPublicCatalog.test.ts @@ -58,6 +58,7 @@ describe("exportDtdPublicCatalog", () => { version: descriptorMock.version, }, technology: eserviceMock.technology.toUpperCase(), + producerId: producerMock.id, producerName: producerMock.name, id: eserviceMock.id, name: eserviceMock.name, From c6d62814f973d90655e3af6a3add7d2ee2fdcf7b Mon Sep 17 00:00:00 2001 From: "renovate-pagopa[bot]" <164534245+renovate-pagopa[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:14:45 +0100 Subject: [PATCH 095/126] Bump express from 4.19.2 to 4.20.0 (#1200) Co-authored-by: renovate-pagopa[bot] <164534245+renovate-pagopa[bot]@users.noreply.github.com> Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- packages/delegation-process/package.json | 2 +- pnpm-lock.yaml | 96 +----------------------- 2 files changed, 4 insertions(+), 94 deletions(-) diff --git a/packages/delegation-process/package.json b/packages/delegation-process/package.json index 75c3a58051..5dee8878df 100644 --- a/packages/delegation-process/package.json +++ b/packages/delegation-process/package.json @@ -40,7 +40,7 @@ "@zodios/core": "10.9.6", "@zodios/express": "10.6.1", "dotenv-flow": "4.1.0", - "express": "4.19.2", + "express": "4.20.0", "mongodb": "6.7.0", "openapi-zod-client": "1.18.1", "pagopa-interop-api-clients": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 743b268d8e..7754fb20e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1856,13 +1856,13 @@ importers: version: 10.9.6(axios@1.7.4)(zod@3.23.8) '@zodios/express': specifier: 10.6.1 - version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8) + version: 10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8) dotenv-flow: specifier: 4.1.0 version: 4.1.0 express: - specifier: 4.19.2 - version: 4.19.2 + specifier: 4.20.0 + version: 4.20.0 mongodb: specifier: 6.7.0 version: 6.7.0(@aws-sdk/credential-providers@3.609.0)(socks@2.8.3) @@ -4957,10 +4957,6 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -5619,10 +5615,6 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} - express@4.19.2: - resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} - engines: {node: '>= 0.10.0'} - express@4.20.0: resolution: {integrity: sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==} engines: {node: '>= 0.10.0'} @@ -6294,9 +6286,6 @@ packages: resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} engines: {node: '>=16.10'} - merge-descriptors@1.0.1: - resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} - merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -6624,9 +6613,6 @@ packages: path-to-regexp@0.1.10: resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} - path-to-regexp@0.1.7: - resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} - path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -6942,10 +6928,6 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} - serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} - engines: {node: '>= 0.8.0'} - serve-static@1.16.0: resolution: {integrity: sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==} engines: {node: '>= 0.8.0'} @@ -10326,12 +10308,6 @@ snapshots: axios: 1.7.4 zod: 3.23.8 - '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.19.2)(zod@3.23.8)': - dependencies: - '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) - express: 4.19.2 - zod: 3.23.8 - '@zodios/express@10.6.1(@zodios/core@10.9.6(axios@1.7.4)(zod@3.23.8))(express@4.20.0)(zod@3.23.8)': dependencies: '@zodios/core': 10.9.6(axios@1.7.4)(zod@3.23.8) @@ -10615,23 +10591,6 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.2: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -11454,42 +11413,6 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 - express@4.19.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.2 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.6.0 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.2.0 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.1 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.7 - proxy-addr: 2.0.7 - qs: 6.11.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - express@4.20.0: dependencies: accepts: 1.3.8 @@ -12199,8 +12122,6 @@ snapshots: meow@12.1.1: {} - merge-descriptors@1.0.1: {} - merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -12505,8 +12426,6 @@ snapshots: path-to-regexp@0.1.10: {} - path-to-regexp@0.1.7: {} - path-to-regexp@6.3.0: {} path-type@4.0.0: {} @@ -12888,15 +12807,6 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: - dependencies: - encodeurl: 1.0.2 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.18.0 - transitivePeerDependencies: - - supports-color - serve-static@1.16.0: dependencies: encodeurl: 1.0.2 From 72c429a0f5eae816496bd5278ff6f4376c8a2656 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Wed, 8 Jan 2025 12:32:46 +0100 Subject: [PATCH 096/126] Fix platformstate writers (#1337) Co-authored-by: Roberto Taglioni --- .../agreement-platformstate-writer/src/consumerServiceV1.ts | 3 +-- .../catalog-platformstate-writer/src/consumerServiceV1.ts | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 6cd0f00fb3..efe3c86426 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -78,7 +78,7 @@ export async function handleMessageV1( await match(agreement.state) // eslint-disable-next-line sonarjs/no-identical-functions - .with(agreementState.active, async () => { + .with(agreementState.active, agreementState.suspended, async () => { // this case is for agreement upgraded const agreement = parseAgreement(msg.data.agreement); await handleUpgrade(agreement, dynamoDBClient, msg.version, logger); @@ -89,7 +89,6 @@ export async function handleMessageV1( agreementState.missingCertifiedAttributes, agreementState.pending, agreementState.rejected, - agreementState.suspended, () => Promise.resolve() ) .exhaustive(); diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts index e8a9044cc2..f75bc87ed8 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts @@ -17,6 +17,7 @@ import { deleteCatalogEntry, descriptorStateToItemState, readCatalogEntry, + updateDescriptorInfoInTokenGenerationStatesTable, updateDescriptorStateInPlatformStatesEntry, updateDescriptorStateInTokenGenerationStatesTable, writeCatalogEntry, @@ -76,9 +77,11 @@ export async function handleMessageV1( eserviceId, descriptorId: descriptor.id, }); - await updateDescriptorStateInTokenGenerationStatesTable( + await updateDescriptorInfoInTokenGenerationStatesTable( eserviceId_descriptorId, descriptorStateToItemState(descriptor.state), + descriptor.voucherLifespan, + descriptor.audience, dynamoDBClient ); }) From c0aa7d3f2db4cfb5f1a23e6f91a4ad32f73da820 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Wed, 8 Jan 2025 18:22:00 +0100 Subject: [PATCH 097/126] Fix agreement timestamp for upgrade (#1345) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 8 +++++--- .../src/consumerServiceV2.ts | 7 +++---- .../test/consumerServiceV1.integration.test.ts | 7 +++++-- .../test/consumerServiceV2.integration.test.ts | 12 ++++++++---- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index efe3c86426..46a412928e 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -257,12 +257,14 @@ const handleUpgrade = async ( ); } } else { - const agreementTimestamp = agreement.stamps.activation + const agreementTimestamp = agreement.stamps.upgrade + ? agreement.stamps.upgrade.when.toISOString() + : agreement.stamps.activation ? agreement.stamps.activation.when.toISOString() : agreement.createdAt.toISOString(); - if (agreement.stamps.activation === undefined) { + if (agreement.stamps.upgrade === undefined) { logger.warn( - `Missing agreement activation stamp for agreement with id ${agreement.id}. Using createdAt as fallback.` + `Missing agreement upgrade stamp for agreement with id ${agreement.id}. Using activation stamp or createdAt as fallback.` ); } diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index e71bfea41a..7a3b2433fd 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -188,9 +188,9 @@ export async function handleMessageV2( ); } } else { - if (!agreement.stamps.activation) { + if (!agreement.stamps.upgrade) { throw genericInternalError( - "An activated agreement should have activation stamp" + "An upgraded agreement should have an upgrade stamp" ); } const newAgreementEntry: PlatformStatesAgreementEntry = { @@ -199,8 +199,7 @@ export async function handleMessageV2( version: msg.version, updatedAt: new Date().toISOString(), GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - agreement.stamps.activation.when.toISOString(), + GSISK_agreementTimestamp: agreement.stamps.upgrade.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index fa2616f1bc..7a210628c1 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -1157,6 +1157,10 @@ describe("integration tests V1 events", async () => { when: new Date(), who: generateId(), }, + upgrade: { + when: new Date(), + who: generateId(), + }, }, }; const payload: AgreementAddedV1 = { @@ -1253,8 +1257,7 @@ describe("integration tests V1 events", async () => { consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + GSISK_agreementTimestamp: agreement.stamps.upgrade!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index abe71bc8e4..51224f4722 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -2493,7 +2493,8 @@ describe("integration tests V2 events", async () => { eserviceId, state: agreementState.active, stamps: { - activation: { + activation: previousAgreement.stamps.activation, + upgrade: { when: new Date(), who: generateId(), }, @@ -2537,7 +2538,7 @@ describe("integration tests V2 events", async () => { state: itemState.inactive, GSIPK_consumerId_eserviceId, GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + latestAgreement.stamps.upgrade!.when.toISOString(), }; await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); @@ -2806,6 +2807,10 @@ describe("integration tests V2 events", async () => { when: new Date(), who: generateId(), }, + upgrade: { + when: new Date(), + who: generateId(), + }, }, }; const payload: AgreementUpgradedV2 = { @@ -2902,8 +2907,7 @@ describe("integration tests V2 events", async () => { consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + GSISK_agreementTimestamp: agreement.stamps.upgrade!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); From 1855fd1404c9f295a807746b95f7da5abdcdbf08 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:12:19 +0100 Subject: [PATCH 098/126] Add debugging logs in authorization-platformstate-writer (#1346) --- .../src/consumerServiceV1.ts | 35 +++++--- .../src/consumerServiceV2.ts | 35 +++++--- .../src/utils.ts | 79 +++++++++++++----- .../consumerServiceV1.integration.test.ts | 81 +++++++++++++++---- .../consumerServiceV2.integration.test.ts | 75 +++++++++++++---- .../test/utils.test.ts | 81 ++++++++++++++----- 6 files changed, 299 insertions(+), 87 deletions(-) diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index d3a53654e3..93c154d6f7 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -172,7 +172,8 @@ export async function handleMessageV1( }; await upsertTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + logger ); return tokenGenStatesConsumerClient; }) @@ -198,6 +199,7 @@ export async function handleMessageV1( purposeEntry: purposeEntry2, agreementEntry: agreementEntry2, catalogEntry: catalogEntry2, + logger, }); }) ); @@ -217,7 +219,8 @@ export async function handleMessageV1( }; await upsertTokenGenStatesConsumerClient( tokenGenStatesConsumerClientWithoutPurpose, - dynamoDBClient + dynamoDBClient, + logger ); } }) @@ -236,7 +239,8 @@ export async function handleMessageV1( }; await upsertTokenGenStatesApiClient( tokenGenStatesApiClient, - dynamoDBClient + dynamoDBClient, + logger ); }) .exhaustive(); @@ -262,7 +266,11 @@ export async function handleMessageV1( await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); const GSIPK_kid = makeGSIPKKid(msg.data.keyId); - await deleteEntriesFromTokenGenStatesByKid(GSIPK_kid, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByKid( + GSIPK_kid, + dynamoDBClient, + logger + ); } }) .with({ type: "ClientPurposeAdded" }, async (msg) => { @@ -328,11 +336,13 @@ export async function handleMessageV1( await upsertTokenGenStatesConsumerClient( newTokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + logger ); await deleteClientEntryFromTokenGenerationStates( entry.PK, - dynamoDBClient + dynamoDBClient, + logger ); return newTokenGenStatesConsumerClient; }) @@ -352,7 +362,8 @@ export async function handleMessageV1( await upsertTokenGenStatesConsumerClient( newTokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + logger ); seenKids.add(kid); return newTokenGenStatesConsumerClient; @@ -388,6 +399,7 @@ export async function handleMessageV1( purposeEntry: purposeEntry2, agreementEntry: agreementEntry2, catalogEntry: catalogEntry2, + logger, }); }) ); @@ -427,12 +439,14 @@ export async function handleMessageV1( if (updatedPurposeIds.length > 0) { await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( GSIPK_clientId_purposeId, - dynamoDBClient + dynamoDBClient, + logger ); } else { await convertEntriesToClientKidInTokenGenerationStates( GSIPK_clientId_purposeId, - dynamoDBClient + dynamoDBClient, + logger ); } } @@ -446,7 +460,8 @@ export async function handleMessageV1( const GSIPK_clientId = clientId; await deleteEntriesFromTokenGenStatesByClientId( GSIPK_clientId, - dynamoDBClient + dynamoDBClient, + logger ); }) .with( diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 1df82f5d35..2529f135a0 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -153,7 +153,8 @@ export async function handleMessageV2( }; await upsertTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + logger ); return tokenGenStatesConsumerClient; }) @@ -179,6 +180,7 @@ export async function handleMessageV2( purposeEntry: purposeEntry2, agreementEntry: agreementEntry2, catalogEntry: catalogEntry2, + logger, }); }) ); @@ -198,7 +200,8 @@ export async function handleMessageV2( }; await upsertTokenGenStatesConsumerClient( tokenGenStatesConsumerClientWithoutPurpose, - dynamoDBClient + dynamoDBClient, + logger ); } }) @@ -217,7 +220,8 @@ export async function handleMessageV2( }; await upsertTokenGenStatesApiClient( tokenGenStatesApiClient, - dynamoDBClient + dynamoDBClient, + logger ); }) .exhaustive(); @@ -243,7 +247,11 @@ export async function handleMessageV2( } const GSIPK_kid = makeGSIPKKid(msg.data.kid); - await deleteEntriesFromTokenGenStatesByKid(GSIPK_kid, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByKid( + GSIPK_kid, + dynamoDBClient, + logger + ); }) .with({ type: "ClientPurposeAdded" }, async (msg) => { const client = parseClient(msg.data.client, msg.type); @@ -310,11 +318,13 @@ export async function handleMessageV2( await upsertTokenGenStatesConsumerClient( newTokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + logger ); await deleteClientEntryFromTokenGenerationStates( entry.PK, - dynamoDBClient + dynamoDBClient, + logger ); return newTokenGenStatesConsumerClient; }) @@ -334,7 +344,8 @@ export async function handleMessageV2( await upsertTokenGenStatesConsumerClient( newTokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + logger ); seenKids.add(kid); return newTokenGenStatesConsumerClient; @@ -370,6 +381,7 @@ export async function handleMessageV2( purposeEntry: purposeEntry2, agreementEntry: agreementEntry2, catalogEntry: catalogEntry2, + logger, }); }) ); @@ -399,12 +411,14 @@ export async function handleMessageV2( if (client.purposes.length > 0) { await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( GSIPK_clientId_purposeId, - dynamoDBClient + dynamoDBClient, + logger ); } else { await convertEntriesToClientKidInTokenGenerationStates( GSIPK_clientId_purposeId, - dynamoDBClient + dynamoDBClient, + logger ); } } @@ -418,7 +432,8 @@ export async function handleMessageV2( const GSIPK_clientId = client.id; await deleteEntriesFromTokenGenStatesByClientId( GSIPK_clientId, - dynamoDBClient + dynamoDBClient, + logger ); }) .with( diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index d9f1faea66..ef2efc645d 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -58,7 +58,8 @@ import { config } from "./config/config.js"; export const deleteEntriesFromTokenGenStatesByKid = async ( GSIPK_kid: GSIPKKid, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const runPaginatedQuery = async ( GSIPK_kid: GSIPKKid, @@ -100,7 +101,8 @@ export const deleteEntriesFromTokenGenStatesByKid = async ( for (const entry of tokenGenStatesEntries.data) { await deleteClientEntryFromTokenGenerationStates( entry.PK, - dynamoDBClient + dynamoDBClient, + logger ); } @@ -133,7 +135,8 @@ export const deleteClientEntryFromPlatformStates = async ( export const deleteEntriesFromTokenGenStatesByClientId = async ( GSIPK_clientId: ClientId, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const runPaginatedQuery = async ( GSIPK_clientId: ClientId, @@ -176,7 +179,8 @@ export const deleteEntriesFromTokenGenStatesByClientId = async ( for (const entry of tokenGenStatesEntries.data) { await deleteClientEntryFromTokenGenerationStates( entry.PK, - dynamoDBClient + dynamoDBClient, + logger ); } @@ -197,7 +201,8 @@ export const deleteClientEntryFromTokenGenerationStates = async ( entryToDeletePK: | TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: DeleteItemInput = { Key: { @@ -206,7 +211,11 @@ export const deleteClientEntryFromTokenGenerationStates = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new DeleteItemCommand(input); - await dynamoDBClient.send(command); + logger.warn( + `Deleting entry ${entryToDeletePK}. Result ${JSON.stringify( + await dynamoDBClient.send(command) + )}` + ); }; export const readPlatformClientEntry = async ( @@ -288,7 +297,8 @@ const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const runPaginatedQuery = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, @@ -304,7 +314,8 @@ export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( for (const entry of res.tokenGenStatesEntries) { await deleteClientEntryFromTokenGenerationStates( entry.PK, - dynamoDBClient + dynamoDBClient, + logger ); } @@ -321,7 +332,8 @@ export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( export const convertEntriesToClientKidInTokenGenerationStates = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const runPaginatedQuery = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, @@ -350,12 +362,13 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( }; // write the new one - await writeTokenGenStatesConsumerClient(newEntry, dynamoDBClient); + await writeTokenGenStatesConsumerClient(newEntry, dynamoDBClient, logger); // delete the old one await deleteClientEntryFromTokenGenerationStates( entry.PK, - dynamoDBClient + dynamoDBClient, + logger ); } @@ -377,7 +390,8 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( export const writeTokenGenStatesApiClient = async ( tokenGenStatesApiClient: TokenGenerationStatesApiClient, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", @@ -407,7 +421,11 @@ export const writeTokenGenStatesApiClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - await dynamoDBClient.send(command); + logger.warn( + `Writing api client ${tokenGenStatesApiClient.PK}. Result ${JSON.stringify( + await dynamoDBClient.send(command) + )}` + ); }; export const readPlatformCatalogEntry = async ( @@ -508,7 +526,8 @@ export const readPlatformPurposeEntry = async ( export const upsertTokenGenStatesConsumerClient = async ( tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { Item: { @@ -618,12 +637,17 @@ export const upsertTokenGenStatesConsumerClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - await dynamoDBClient.send(command); + logger.warn( + `Upserting consumer client ${ + tokenGenStatesConsumerClient.PK + }. Result ${JSON.stringify(await dynamoDBClient.send(command))}` + ); }; export const writeTokenGenStatesConsumerClient = async ( tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", @@ -734,7 +758,11 @@ export const writeTokenGenStatesConsumerClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - await dynamoDBClient.send(command); + logger.warn( + `Writing consumer client ${ + tokenGenStatesConsumerClient.PK + }. Result ${JSON.stringify(await dynamoDBClient.send(command))}` + ); }; export const clientKindToTokenGenerationStatesClientKind = ( @@ -1009,7 +1037,8 @@ export const upsertPlatformClientEntry = async ( export const upsertTokenGenStatesApiClient = async ( entry: TokenGenerationStatesApiClient, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { Item: { @@ -1038,7 +1067,11 @@ export const upsertTokenGenStatesApiClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - await dynamoDBClient.send(command); + logger.warn( + `Upserting api client ${entry.PK}. Result ${JSON.stringify( + await dynamoDBClient.send(command) + )}` + ); }; export const updateTokenGenStatesDataForSecondRetrieval = async ({ @@ -1047,9 +1080,11 @@ export const updateTokenGenStatesDataForSecondRetrieval = async ({ purposeEntry, agreementEntry, catalogEntry, + logger, }: { dynamoDBClient: DynamoDBClient; entry: TokenGenerationStatesConsumerClient; + logger: Logger; purposeEntry?: PlatformStatesPurposeEntry; agreementEntry?: PlatformStatesAgreementGSIAgreement; catalogEntry?: PlatformStatesCatalogEntry; @@ -1126,7 +1161,11 @@ export const updateTokenGenStatesDataForSecondRetrieval = async ({ ReturnValues: "NONE", }; const command = new UpdateItemCommand(input); - await dynamoDBClient.send(command); + logger.warn( + `Updating entry ${entry.PK}. Result ${JSON.stringify( + await dynamoDBClient.send(command) + )}` + ); } }; diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index 2b32dd102f..47e0c64cb0 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -291,7 +291,11 @@ describe("integration tests V1 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -353,7 +357,11 @@ describe("integration tests V1 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1051,7 +1059,11 @@ describe("integration tests V1 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1156,8 +1168,16 @@ describe("integration tests V1 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); - await writeTokenGenStatesApiClient(tokenClientEntry2, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry1, + dynamoDBClient, + genericLogger + ); + await writeTokenGenStatesApiClient( + tokenClientEntry2, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1250,7 +1270,11 @@ describe("integration tests V1 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kidToRemove), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1421,7 +1445,8 @@ describe("integration tests V1 events", async () => { ); await writeTokenGenStatesApiClient( tokenClientEntryWithOtherKid, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1504,7 +1529,11 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1567,7 +1596,11 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1642,7 +1675,11 @@ describe("integration tests V1 events", async () => { tokenConsumerClient, dynamoDBClient ); - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -2084,7 +2121,8 @@ describe("integration tests V1 events", async () => { ); await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -2398,7 +2436,8 @@ describe("integration tests V1 events", async () => { ); await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -2527,7 +2566,11 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -2583,7 +2626,11 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -2684,7 +2731,11 @@ describe("integration tests V1 events", async () => { GSIPK_purposeId: purposeId2, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await writeTokenGenStatesConsumerClient( tokenConsumerClient1, dynamoDBClient diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index aa81fe9139..052693ebd0 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -140,7 +140,11 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(key.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1092,7 +1096,11 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1187,7 +1195,11 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(oldKey.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1288,8 +1300,16 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(addedKey.kid), }; - await writeTokenGenStatesApiClient(tokenClientEntry1, dynamoDBClient); - await writeTokenGenStatesApiClient(tokenClientEntry2, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry1, + dynamoDBClient, + genericLogger + ); + await writeTokenGenStatesApiClient( + tokenClientEntry2, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1382,7 +1402,11 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_kid: makeGSIPKKid(kidToRemove), }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1552,7 +1576,8 @@ describe("integration tests V2 events", async () => { ); await writeTokenGenStatesApiClient( tokenClientEntryWithOtherKid, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1629,7 +1654,11 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1695,7 +1724,11 @@ describe("integration tests V2 events", async () => { tokenConsumerClient, dynamoDBClient ); - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -2122,7 +2155,8 @@ describe("integration tests V2 events", async () => { ); await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -2429,7 +2463,8 @@ describe("integration tests V2 events", async () => { ); await writeTokenGenStatesApiClient( tokenClientEntryWithOtherClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -2558,7 +2593,11 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -2615,7 +2654,11 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -2716,7 +2759,11 @@ describe("integration tests V2 events", async () => { GSIPK_purposeId: purposeId2, }; - await writeTokenGenStatesApiClient(tokenClientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); await writeTokenGenStatesConsumerClient( tokenConsumerClient1, dynamoDBClient diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 15c9c51571..30c48165d8 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -123,7 +123,11 @@ describe("utils", () => { ...getMockTokenGenStatesConsumerClient(), }; - await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, dynamoDBClient @@ -133,7 +137,11 @@ describe("utils", () => { dynamoDBClient ); - await deleteEntriesFromTokenGenStatesByKid(kid, dynamoDBClient); + await deleteEntriesFromTokenGenStatesByKid( + kid, + dynamoDBClient, + genericLogger + ); const result = await readAllTokenGenStatesItems(dynamoDBClient); expect(result).toEqual([otherConsumerClient]); @@ -176,7 +184,11 @@ describe("utils", () => { const otherConsumerClient: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(), }; - await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, dynamoDBClient @@ -188,7 +200,8 @@ describe("utils", () => { await deleteEntriesFromTokenGenStatesByClientId( GSIPK_clientId, - dynamoDBClient + dynamoDBClient, + genericLogger ); const result = await readAllTokenGenStatesItems(dynamoDBClient); @@ -206,7 +219,11 @@ describe("utils", () => { ...getMockTokenGenStatesConsumerClient(), }; - await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, dynamoDBClient @@ -214,7 +231,8 @@ describe("utils", () => { await deleteClientEntryFromTokenGenerationStates( clientEntry.PK, - dynamoDBClient + dynamoDBClient, + genericLogger ); const result = await readAllTokenGenStatesItems(dynamoDBClient); @@ -230,7 +248,11 @@ describe("utils", () => { ...getMockTokenGenStatesConsumerClient(), }; - await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, dynamoDBClient @@ -238,7 +260,8 @@ describe("utils", () => { await deleteClientEntryFromTokenGenerationStates( tokenGenStatesConsumerClient.PK, - dynamoDBClient + dynamoDBClient, + genericLogger ); const result = await readAllTokenGenStatesItems(dynamoDBClient); @@ -291,7 +314,8 @@ describe("utils", () => { await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( GSIPK_clientId_purposeId, - dynamoDBClient + dynamoDBClient, + genericLogger ); const result = await readAllTokenGenStatesItems(dynamoDBClient); @@ -350,7 +374,8 @@ describe("utils", () => { await convertEntriesToClientKidInTokenGenerationStates( GSIPK_clientId_purposeId, - dynamoDBClient + dynamoDBClient, + genericLogger ); const expectedEntry1: TokenGenerationStatesConsumerClient = { @@ -386,7 +411,11 @@ describe("utils", () => { describe("writeTokenGenStatesApiClient", () => { it("should succeed if the entry doesn't exist", async () => { const clientEntry = getMockTokenGenStatesApiClient(); - await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); const result = await readAllTokenGenStatesItems(dynamoDBClient); expect(result).toEqual([clientEntry]); @@ -394,10 +423,14 @@ describe("utils", () => { it("should throw error if the entry already exists", async () => { const clientEntry = getMockTokenGenStatesApiClient(); - await writeTokenGenStatesApiClient(clientEntry, dynamoDBClient); + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); expect( - writeTokenGenStatesApiClient(clientEntry, dynamoDBClient) + writeTokenGenStatesApiClient(clientEntry, dynamoDBClient, genericLogger) ).rejects.toThrowError(); }); }); @@ -451,7 +484,8 @@ describe("utils", () => { await upsertTokenGenStatesConsumerClient( tokenGenStatesConsumerClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); @@ -472,7 +506,11 @@ describe("utils", () => { ...tokenGenStatesConsumerClient, descriptorState: itemState.inactive, }; - await upsertTokenGenStatesConsumerClient(updatedEntry, dynamoDBClient); + await upsertTokenGenStatesConsumerClient( + updatedEntry, + dynamoDBClient, + genericLogger + ); const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); expect(resultAfter).toEqual([updatedEntry]); @@ -666,7 +704,8 @@ describe("utils", () => { await upsertTokenGenStatesApiClient( tokenGenStatesApiClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); @@ -679,14 +718,19 @@ describe("utils", () => { }; await writeTokenGenStatesApiClient( tokenGenStatesApiClient, - dynamoDBClient + dynamoDBClient, + genericLogger ); const updatedEntry: TokenGenerationStatesApiClient = { ...tokenGenStatesApiClient, clientKind: clientKindTokenGenStates.api, }; - await upsertTokenGenStatesApiClient(updatedEntry, dynamoDBClient); + await upsertTokenGenStatesApiClient( + updatedEntry, + dynamoDBClient, + genericLogger + ); const resultAfter = await readAllTokenGenStatesItems(dynamoDBClient); expect(resultAfter).toEqual([updatedEntry]); @@ -836,6 +880,7 @@ describe("utils", () => { purposeEntry: platformPurposeEntry, agreementEntry: platformAgreementEntry, catalogEntry: platformCatalogEntry, + logger: genericLogger, }); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( From e7bf50bf81d1c9b19ad403f4f3b8b8d020a96c0f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 9 Jan 2025 13:01:10 +0100 Subject: [PATCH 099/126] Fix authorization platformstate writer (#1348) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../token-generation-states-dynamo-db.json | 10 +- .../src/consumerServiceV1.ts | 22 +- .../src/consumerServiceV2.ts | 28 ++- .../src/utils.ts | 48 +++-- .../consumerServiceV1.integration.test.ts | 163 +++++++++++---- .../consumerServiceV2.integration.test.ts | 190 ++++++++++++++---- .../test/utils.test.ts | 51 +++-- .../authorizationServer.integration.test.ts | 7 +- .../src/services/toolService.ts | 6 +- .../commons-test/src/setupDynamoDBtables.ts | 10 +- packages/commons-test/src/testUtils.ts | 8 +- .../src/tokenGenerationReadmodelUtils.ts | 8 +- packages/models/src/brandedIds.ts | 6 +- .../src/token-generation-readmodel/commons.ts | 11 +- .../token-generation-states-entry.ts | 38 ++-- 15 files changed, 428 insertions(+), 178 deletions(-) diff --git a/docker/dynamo-db/schema/token-generation-states-dynamo-db.json b/docker/dynamo-db/schema/token-generation-states-dynamo-db.json index 319a6fa58d..3fa09e8b6f 100644 --- a/docker/dynamo-db/schema/token-generation-states-dynamo-db.json +++ b/docker/dynamo-db/schema/token-generation-states-dynamo-db.json @@ -22,7 +22,7 @@ "AttributeType": "S" }, { - "AttributeName": "GSIPK_kid", + "AttributeName": "GSIPK_clientId_kid", "AttributeType": "S" }, { @@ -103,15 +103,15 @@ "consumerId", "clientKind", "publicKey", - "GSIPK_kid" + "GSIPK_clientId_kid" ] } }, { - "IndexName": "Kid", + "IndexName": "ClientKid", "KeySchema": [ { - "AttributeName": "GSIPK_kid", + "AttributeName": "GSIPK_clientId_kid", "KeyType": "HASH" } ], @@ -131,7 +131,7 @@ "ProjectionType": "INCLUDE", "NonKeyAttributes": [ "GSIPK_clientId", - "GSIPK_kid", + "GSIPK_clientId_kid", "GSIPK_purposeId", "consumerId", "clientKind", diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 93c154d6f7..4d87fcadad 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -13,7 +13,7 @@ import { makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, - makeGSIPKKid, + makeGSIPKClientIdKid, makePlatformStatesClientPK, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, @@ -33,7 +33,7 @@ import { deleteClientEntryFromPlatformStates, deleteClientEntryFromTokenGenerationStates, deleteEntriesFromTokenGenStatesByClientId, - deleteEntriesFromTokenGenStatesByKid, + deleteEntriesFromTokenGenStatesByClientIdKid, deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, extractAgreementIdFromAgreementPK, extractKidFromTokenGenStatesEntryPK, @@ -129,7 +129,10 @@ export async function handleMessageV1( publicKey: pem, updatedAt: new Date().toISOString(), GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, purposeId, @@ -214,7 +217,7 @@ export async function handleMessageV1( clientKind: clientKindTokenGenStates.consumer, publicKey: pem, GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId, kid }), updatedAt: new Date().toISOString(), }; await upsertTokenGenStatesConsumerClient( @@ -234,7 +237,7 @@ export async function handleMessageV1( clientKind: clientKindTokenGenStates.api, publicKey: pem, GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId, kid }), updatedAt: new Date().toISOString(), }; await upsertTokenGenStatesApiClient( @@ -265,9 +268,12 @@ export async function handleMessageV1( }; await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); - const GSIPK_kid = makeGSIPKKid(msg.data.keyId); - await deleteEntriesFromTokenGenStatesByKid( - GSIPK_kid, + const GSIPK_clientId_kid = makeGSIPKClientIdKid({ + clientId, + kid: msg.data.keyId, + }); + await deleteEntriesFromTokenGenStatesByClientIdKid( + GSIPK_clientId_kid, dynamoDBClient, logger ); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 2529f135a0..1db8722148 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -10,7 +10,7 @@ import { makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, - makeGSIPKKid, + makeGSIPKClientIdKid, makePlatformStatesClientPK, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, @@ -28,7 +28,7 @@ import { convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, deleteEntriesFromTokenGenStatesByClientId, - deleteEntriesFromTokenGenStatesByKid, + deleteEntriesFromTokenGenStatesByClientIdKid, deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, readPlatformClientEntry, deleteClientEntryFromTokenGenerationStates, @@ -111,7 +111,10 @@ export async function handleMessageV2( publicKey: pem, updatedAt: new Date().toISOString(), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(msg.data.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: msg.data.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId, @@ -195,7 +198,10 @@ export async function handleMessageV2( clientKind: clientKindTokenGenStates.consumer, publicKey: pem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(msg.data.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: msg.data.kid, + }), updatedAt: new Date().toISOString(), }; await upsertTokenGenStatesConsumerClient( @@ -215,7 +221,10 @@ export async function handleMessageV2( clientKind: clientKindTokenGenStates.api, publicKey: pem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(msg.data.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: msg.data.kid, + }), updatedAt: new Date().toISOString(), }; await upsertTokenGenStatesApiClient( @@ -246,9 +255,12 @@ export async function handleMessageV2( await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); } - const GSIPK_kid = makeGSIPKKid(msg.data.kid); - await deleteEntriesFromTokenGenStatesByKid( - GSIPK_kid, + const GSIPK_clientId_kid = makeGSIPKClientIdKid({ + clientId: client.id, + kid: msg.data.kid, + }); + await deleteEntriesFromTokenGenStatesByClientIdKid( + GSIPK_clientId_kid, dynamoDBClient, logger ); diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index ef2efc645d..b026892967 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -25,7 +25,7 @@ import { genericInternalError, GSIPKClientIdPurposeId, GSIPKConsumerIdEServiceId, - GSIPKKid, + GSIPKClientIdKid, makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, @@ -49,29 +49,29 @@ import { TokenGenStatesConsumerClientGSIClient, TokenGenStatesConsumerClientGSIClientPurpose, TokenGenStatesGenericClientGSIClient, - TokenGenStatesGenericClientGSIKid, + TokenGenStatesGenericClientGSIClientKid, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; import { z } from "zod"; import { config } from "./config/config.js"; -export const deleteEntriesFromTokenGenStatesByKid = async ( - GSIPK_kid: GSIPKKid, +export const deleteEntriesFromTokenGenStatesByClientIdKid = async ( + GSIPK_clientId_kid: GSIPKClientIdKid, dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { const runPaginatedQuery = async ( - GSIPK_kid: GSIPKKid, + GSIPK_clientId_kid: GSIPKClientIdKid, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { const input: QueryInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - IndexName: "Kid", - KeyConditionExpression: `GSIPK_kid = :gsiValue`, + IndexName: "ClientKid", + KeyConditionExpression: `GSIPK_clientId_kid = :gsiValue`, ExpressionAttributeValues: { - ":gsiValue": { S: GSIPK_kid }, + ":gsiValue": { S: GSIPK_clientId_kid }, }, ExclusiveStartKey: exclusiveStartKey, }; @@ -87,7 +87,7 @@ export const deleteEntriesFromTokenGenStatesByKid = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenStatesGenericClientGSIKid) + .array(TokenGenStatesGenericClientGSIClientKid) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { @@ -108,7 +108,7 @@ export const deleteEntriesFromTokenGenStatesByKid = async ( if (data.LastEvaluatedKey) { await runPaginatedQuery( - GSIPK_kid, + GSIPK_clientId_kid, dynamoDBClient, data.LastEvaluatedKey ); @@ -116,7 +116,7 @@ export const deleteEntriesFromTokenGenStatesByKid = async ( } }; - await runPaginatedQuery(GSIPK_kid, dynamoDBClient, undefined); + await runPaginatedQuery(GSIPK_clientId_kid, dynamoDBClient, undefined); }; export const deleteClientEntryFromPlatformStates = async ( @@ -351,13 +351,13 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( const newEntry: TokenGenerationStatesConsumerClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: entry.GSIPK_clientId, - kid: entry.GSIPK_kid, + kid: extractKidFromGSIClientKid(entry.GSIPK_clientId_kid), }), consumerId: entry.consumerId, clientKind: entry.clientKind, publicKey: entry.publicKey, GSIPK_clientId: entry.GSIPK_clientId, - GSIPK_kid: entry.GSIPK_kid, + GSIPK_clientId_kid: entry.GSIPK_clientId_kid, updatedAt: new Date().toISOString(), }; @@ -414,8 +414,8 @@ export const writeTokenGenStatesApiClient = async ( GSIPK_clientId: { S: tokenGenStatesApiClient.GSIPK_clientId, }, - GSIPK_kid: { - S: tokenGenStatesApiClient.GSIPK_kid, + GSIPK_clientId_kid: { + S: tokenGenStatesApiClient.GSIPK_clientId_kid, }, }, TableName: config.tokenGenerationReadModelTableNameTokenGeneration, @@ -595,8 +595,8 @@ export const upsertTokenGenStatesConsumerClient = async ( GSIPK_clientId: { S: tokenGenStatesConsumerClient.GSIPK_clientId, }, - GSIPK_kid: { - S: tokenGenStatesConsumerClient.GSIPK_kid, + GSIPK_clientId_kid: { + S: tokenGenStatesConsumerClient.GSIPK_clientId_kid, }, ...(tokenGenStatesConsumerClient.GSIPK_clientId_purposeId ? { @@ -716,8 +716,8 @@ export const writeTokenGenStatesConsumerClient = async ( GSIPK_clientId: { S: tokenGenStatesConsumerClient.GSIPK_clientId, }, - GSIPK_kid: { - S: tokenGenStatesConsumerClient.GSIPK_kid, + GSIPK_clientId_kid: { + S: tokenGenStatesConsumerClient.GSIPK_clientId_kid, }, ...(tokenGenStatesConsumerClient.GSIPK_clientId_purposeId ? { @@ -933,6 +933,10 @@ export const extractAgreementIdFromAgreementPK = ( return result.data; }; +export const extractKidFromGSIClientKid = ( + GSIPK_clientId_kid: GSIPKClientIdKid +): string => GSIPK_clientId_kid.split("#")[1]; + export const retrievePlatformStatesByPurpose = async ( purposeId: PurposeId, dynamoDBClient: DynamoDBClient, @@ -1057,8 +1061,8 @@ export const upsertTokenGenStatesApiClient = async ( GSIPK_clientId: { S: entry.GSIPK_clientId, }, - GSIPK_kid: { - S: entry.GSIPK_kid, + GSIPK_clientId_kid: { + S: entry.GSIPK_clientId_kid, }, updatedAt: { S: entry.updatedAt, @@ -1262,7 +1266,7 @@ export const createTokenGenStatesConsumerClient = ({ clientKind: clientKindTokenGenStates.consumer, publicKey: tokenGenStatesClient.publicKey, GSIPK_clientId: tokenGenStatesClient.GSIPK_clientId, - GSIPK_kid: tokenGenStatesClient.GSIPK_kid, + GSIPK_clientId_kid: tokenGenStatesClient.GSIPK_clientId_kid, GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, purposeId, diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index 47e0c64cb0..f4769325a2 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -45,7 +45,7 @@ import { makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, - makeGSIPKKid, + makeGSIPKClientIdKid, makePlatformStatesAgreementPK, makePlatformStatesClientPK, makePlatformStatesEServiceDescriptorPK, @@ -289,7 +289,10 @@ describe("integration tests V1 events", async () => { const tokenClientEntry: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(key.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -355,7 +358,10 @@ describe("integration tests V1 events", async () => { const tokenClientEntry: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(key.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -550,11 +556,15 @@ describe("integration tests V1 events", async () => { clientId: client.id, purposeId: purpose2.id, }); + const GSIPK_clientId_kid = makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }); const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_purposeId: purpose1.id, }; @@ -562,7 +572,7 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_purposeId: purpose2.id, }; @@ -617,7 +627,10 @@ describe("integration tests V1 events", async () => { purposeState: platformPurposeEntry1.state, purposeVersionId: platformPurposeEntry1.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose1.id, @@ -648,7 +661,10 @@ describe("integration tests V1 events", async () => { purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -865,7 +881,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_purposeId: purpose1.id, }; @@ -873,7 +892,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_purposeId: purpose2.id, }; @@ -881,7 +903,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK3), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, GSIPK_purposeId: purpose1.id, }; @@ -889,7 +914,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK4), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, GSIPK_purposeId: purpose2.id, }; @@ -952,7 +980,10 @@ describe("integration tests V1 events", async () => { purposeState: platformPurposeEntry1.state, purposeVersionId: platformPurposeEntry1.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose1.id, @@ -983,7 +1014,10 @@ describe("integration tests V1 events", async () => { purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -1057,7 +1091,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -1092,7 +1129,10 @@ describe("integration tests V1 events", async () => { clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), updatedAt: new Date().toISOString(), }; @@ -1161,12 +1201,18 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), }; const tokenClientEntry2: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK2), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry1, @@ -1206,7 +1252,10 @@ describe("integration tests V1 events", async () => { clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), updatedAt: new Date().toISOString(), }; @@ -1268,7 +1317,10 @@ describe("integration tests V1 events", async () => { const tokenClientEntry: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kidToRemove, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -1333,7 +1385,10 @@ describe("integration tests V1 events", async () => { const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kidToRemove, + }), GSIPK_clientId: client.id, }; const tokenConsumerClientWithOtherKid: TokenGenerationStatesConsumerClient = @@ -1429,14 +1484,20 @@ describe("integration tests V1 events", async () => { const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kidToRemove, + }), GSIPK_clientId: client.id, }; const tokenClientEntryWithOtherKid: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(otherKid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: otherKid, + }), }; await writeTokenGenStatesConsumerClient( @@ -1823,13 +1884,19 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), }; const tokenClientEntry2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), }; const tokenConsumerClientWithOtherClient = getMockTokenGenStatesConsumerClient(); @@ -2098,7 +2165,10 @@ describe("integration tests V1 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purpose1.id, }; const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { @@ -2106,7 +2176,10 @@ describe("integration tests V1 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purpose1.id, }; const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); @@ -2389,7 +2462,10 @@ describe("integration tests V1 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purpose1.id, }; const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { @@ -2397,7 +2473,10 @@ describe("integration tests V1 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purpose1.id, }; const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { @@ -2405,7 +2484,10 @@ describe("integration tests V1 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purpose2.id, }; const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { @@ -2413,7 +2495,10 @@ describe("integration tests V1 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purpose2.id, }; const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); @@ -2690,18 +2775,18 @@ describe("integration tests V1 events", async () => { ); // token-generation-states - const mockClientKidPurpose1 = "mockClientKidPurpose1"; - const mockClientKidPurpose2 = "mockClientKidPurpose2"; + const kid1 = "kid1"; + const kid2 = "kid2"; const tokenClientKidPurposePK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: mockClientKidPurpose1, + kid: kid1, purposeId: purposeId1, }); const tokenClientKidPurposePK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: mockClientKidPurpose2, + kid: kid2, purposeId: purposeId2, }); const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ @@ -2719,7 +2804,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purposeId1, }; @@ -2727,7 +2815,10 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purposeId2, }; diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index 052693ebd0..c3362c18d3 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -43,7 +43,7 @@ import { makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, - makeGSIPKKid, + makeGSIPKClientIdKid, makePlatformStatesAgreementPK, makePlatformStatesClientPK, makePlatformStatesEServiceDescriptorPK, @@ -138,7 +138,10 @@ describe("integration tests V2 events", async () => { const tokenClientEntry: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(key.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -322,7 +325,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_purposeId: purpose1.id, }; @@ -330,7 +336,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_purposeId: purpose2.id, }; @@ -389,7 +398,10 @@ describe("integration tests V2 events", async () => { purposeState: platformPurposeEntry1.state, purposeVersionId: platformPurposeEntry1.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose1.id, @@ -420,7 +432,10 @@ describe("integration tests V2 events", async () => { purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -610,7 +625,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_purposeId: purpose1.id, }; @@ -618,7 +636,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_purposeId: purpose2.id, }; @@ -674,7 +695,10 @@ describe("integration tests V2 events", async () => { purposeState: platformPurposeEntry1.state, purposeVersionId: platformPurposeEntry1.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose1.id, @@ -705,7 +729,10 @@ describe("integration tests V2 events", async () => { purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -917,7 +944,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_purposeId: purpose1.id, }; @@ -925,7 +955,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_purposeId: purpose2.id, }; @@ -933,7 +966,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK3), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId3, GSIPK_purposeId: purpose1.id, }; @@ -941,7 +977,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK4), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId4, GSIPK_purposeId: purpose2.id, }; @@ -1005,7 +1044,10 @@ describe("integration tests V2 events", async () => { purposeState: platformPurposeEntry1.state, purposeVersionId: platformPurposeEntry1.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose1.id, @@ -1036,7 +1078,10 @@ describe("integration tests V2 events", async () => { purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, publicKey: addedKey.encodedPem, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose2.id, @@ -1094,7 +1139,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -1133,7 +1181,10 @@ describe("integration tests V2 events", async () => { clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), updatedAt: new Date().toISOString(), }; @@ -1193,7 +1244,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -1229,7 +1283,10 @@ describe("integration tests V2 events", async () => { clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), updatedAt: new Date().toISOString(), }; @@ -1293,12 +1350,18 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesApiClient(tokenClientKidPK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(oldKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: oldKey.kid, + }), }; const tokenClientEntry2: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK2), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry1, @@ -1339,7 +1402,10 @@ describe("integration tests V2 events", async () => { clientKind: clientKindTokenGenStates.consumer, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(addedKey.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: addedKey.kid, + }), updatedAt: new Date().toISOString(), }; @@ -1400,7 +1466,10 @@ describe("integration tests V2 events", async () => { const tokenClientEntry: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kidToRemove, + }), }; await writeTokenGenStatesApiClient( tokenClientEntry, @@ -1458,7 +1527,10 @@ describe("integration tests V2 events", async () => { const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kidToRemove, + }), GSIPK_clientId: client.id, }; const tokenConsumerClientWithOtherKid: TokenGenerationStatesConsumerClient = @@ -1560,14 +1632,20 @@ describe("integration tests V2 events", async () => { const tokenConsumerClientWithKid: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), - GSIPK_kid: makeGSIPKKid(kidToRemove), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kidToRemove, + }), GSIPK_clientId: client.id, }; const tokenClientEntryWithOtherKid: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(tokenClientKidPK), GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(otherKid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: otherKid, + }), }; await writeTokenGenStatesConsumerClient( @@ -1865,13 +1943,19 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK1), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), }; const tokenClientEntry2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), consumerId: client.consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), }; const tokenConsumerClientWithOtherClient = getMockTokenGenStatesConsumerClient(); @@ -2132,7 +2216,10 @@ describe("integration tests V2 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purpose1.id, }; const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { @@ -2140,7 +2227,10 @@ describe("integration tests V2 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purpose1.id, }; const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); @@ -2416,7 +2506,10 @@ describe("integration tests V2 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purpose1.id, }; const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { @@ -2424,7 +2517,10 @@ describe("integration tests V2 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purpose1.id, }; const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { @@ -2432,7 +2528,10 @@ describe("integration tests V2 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid1, + }), GSIPK_purposeId: purpose2.id, }; const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { @@ -2440,7 +2539,10 @@ describe("integration tests V2 events", async () => { consumerId: client.consumerId, GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: kid2, + }), GSIPK_purposeId: purpose2.id, }; const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); @@ -2718,18 +2820,18 @@ describe("integration tests V2 events", async () => { ); // token-generation-states - const mockClientKidPurpose1 = "mockClientKidPurpose1"; - const mockClientKidPurpose2 = "mockClientKidPurpose2"; + const mockClientKidPurpose1kid = "mockClientKidPurpose1kid"; + const mockClientKidPurpose2kid = "mockClientKidPurpose2kid"; const tokenClientKidPurposePK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: mockClientKidPurpose1, + kid: mockClientKidPurpose1kid, purposeId: purposeId1, }); const tokenClientKidPurposePK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: mockClientKidPurpose2, + kid: mockClientKidPurpose2kid, purposeId: purposeId2, }); const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ @@ -2747,7 +2849,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: mockClientKidPurpose1kid, + }), GSIPK_purposeId: purposeId1, }; @@ -2755,7 +2860,10 @@ describe("integration tests V2 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(mockClientKidPurpose2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: mockClientKidPurpose2kid, + }), GSIPK_purposeId: purposeId2, }; diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 30c48165d8..8ca055d2be 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -31,12 +31,11 @@ import { EService, EServiceId, generateId, - GSIPKKid, itemState, makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, - makeGSIPKKid, + makeGSIPKClientIdKid, makePlatformStatesAgreementPK, makePlatformStatesClientPK, makePlatformStatesEServiceDescriptorPK, @@ -55,7 +54,6 @@ import { TokenGenerationStatesApiClient, TokenGenerationStatesConsumerClient, TokenGenerationStatesGenericClient, - unsafeBrandId, TokenGenStatesConsumerClientGSIClient, } from "pagopa-interop-models"; import { @@ -80,7 +78,7 @@ import { updateTokenGenStatesDataForSecondRetrieval, upsertPlatformClientEntry, writeTokenGenStatesApiClient, - deleteEntriesFromTokenGenStatesByKid, + deleteEntriesFromTokenGenStatesByClientIdKid, writePlatformClientEntry, deleteEntriesFromTokenGenStatesByClientId, deleteClientEntryFromTokenGenerationStates, @@ -107,16 +105,19 @@ describe("utils", () => { vi.useRealTimers(); }); - it("deleteEntriesFromTokenGenStatesByKid", async () => { - const kid = unsafeBrandId("mock kid"); + it("deleteEntriesFromTokenGenStatesByClientIdKid", async () => { + const clientIdkid = makeGSIPKClientIdKid({ + clientId: generateId(), + kid: "kid", + }); const clientEntry: TokenGenerationStatesApiClient = { ...getMockTokenGenStatesApiClient(), - GSIPK_kid: kid, + GSIPK_clientId_kid: clientIdkid, }; const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(), - GSIPK_kid: kid, + GSIPK_clientId_kid: clientIdkid, }; const otherConsumerClient: TokenGenerationStatesConsumerClient = { @@ -137,8 +138,8 @@ describe("utils", () => { dynamoDBClient ); - await deleteEntriesFromTokenGenStatesByKid( - kid, + await deleteEntriesFromTokenGenStatesByClientIdKid( + clientIdkid, dynamoDBClient, genericLogger ); @@ -342,7 +343,10 @@ describe("utils", () => { ...getMockTokenGenStatesConsumerClient(pk1), GSIPK_clientId_purposeId, GSIPK_clientId: clientId, - GSIPK_kid: unsafeBrandId(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid: kid1, + }), }; const pk2 = makeTokenGenerationStatesClientKidPurposePK({ @@ -354,7 +358,10 @@ describe("utils", () => { ...getMockTokenGenStatesConsumerClient(pk2), GSIPK_clientId_purposeId, GSIPK_clientId: clientId, - GSIPK_kid: unsafeBrandId(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid: kid2, + }), }; const tokenGenStatesConsumerClient3 = getMockTokenGenStatesConsumerClient(); @@ -385,7 +392,10 @@ describe("utils", () => { clientKind: clientKindTokenGenStates.consumer, publicKey: tokenGenStatesConsumerClient1.publicKey, GSIPK_clientId: clientId, - GSIPK_kid: unsafeBrandId(kid1), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid: kid1, + }), }; const expectedEntry2: TokenGenerationStatesConsumerClient = { @@ -395,7 +405,10 @@ describe("utils", () => { clientKind: "CONSUMER", publicKey: tokenGenStatesConsumerClient1.publicKey, GSIPK_clientId: clientId, - GSIPK_kid: unsafeBrandId(kid2), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid: kid2, + }), }; const result = await readAllTokenGenStatesItems(dynamoDBClient); @@ -768,7 +781,10 @@ describe("utils", () => { clientKind: clientKindTokenGenStates.consumer, publicKey: "publicKey", GSIPK_clientId: generateId(), - GSIPK_kid: unsafeBrandId("kid"), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: generateId(), + kid: "kid", + }), updatedAt: new Date().toISOString(), }; const entries3 = z @@ -817,7 +833,10 @@ describe("utils", () => { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK), consumerId, GSIPK_clientId: client.id, - GSIPK_kid: makeGSIPKKid(key.kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key.kid, + }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purpose.id, diff --git a/packages/authorization-server/test/authorizationServer.integration.test.ts b/packages/authorization-server/test/authorizationServer.integration.test.ts index e2b2b7b5eb..2370079b9f 100644 --- a/packages/authorization-server/test/authorizationServer.integration.test.ts +++ b/packages/authorization-server/test/authorizationServer.integration.test.ts @@ -22,7 +22,7 @@ import { GeneratedTokenAuditDetails, generateId, itemState, - makeGSIPKKid, + makeGSIPKClientIdKid, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, Purpose, @@ -677,7 +677,10 @@ describe("authorization server tests", () => { agreementState: itemState.active, descriptorState: itemState.active, GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(clientAssertion.header.kid!), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid: clientAssertion.header.kid!, + }), publicKey: publicKeyEncodedPem, }; diff --git a/packages/backend-for-frontend/src/services/toolService.ts b/packages/backend-for-frontend/src/services/toolService.ts index 181c2bc634..510da7aa13 100644 --- a/packages/backend-for-frontend/src/services/toolService.ts +++ b/packages/backend-for-frontend/src/services/toolService.ts @@ -16,7 +16,7 @@ import { ClientId, DescriptorId, EServiceId, - GSIPKKid, + GSIPKClientIdKid, ItemState, makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, @@ -274,7 +274,7 @@ async function retrieveKeyAndEservice( kid: jwt.header.kid, }), clientKind: authorizationApi.ClientKind.enum.API, - GSIPK_kid: unsafeBrandId(jwt.header.kid), + GSIPK_clientId_kid: unsafeBrandId(jwt.header.kid), publicKey: encodedPem, GSIPK_clientId: unsafeBrandId(keyWithClient.client.id), consumerId: unsafeBrandId(keyWithClient.client.consumerId), @@ -336,7 +336,7 @@ async function retrieveKeyAndEservice( }), clientKind: authorizationApi.ClientKind.enum.CONSUMER, GSIPK_clientId: unsafeBrandId(keyWithClient.client.id), - GSIPK_kid: unsafeBrandId(jwt.header.kid), + GSIPK_clientId_kid: unsafeBrandId(jwt.header.kid), publicKey: encodedPem, GSIPK_purposeId: purposeId, consumerId: unsafeBrandId(keyWithClient.client.consumerId), diff --git a/packages/commons-test/src/setupDynamoDBtables.ts b/packages/commons-test/src/setupDynamoDBtables.ts index 93c2911d41..5058539c5b 100644 --- a/packages/commons-test/src/setupDynamoDBtables.ts +++ b/packages/commons-test/src/setupDynamoDBtables.ts @@ -49,7 +49,7 @@ export const buildDynamoDBTables = async ( { AttributeName: "GSIPK_consumerId_eserviceId", AttributeType: "S" }, { AttributeName: "GSIPK_purposeId", AttributeType: "S" }, { AttributeName: "GSIPK_clientId", AttributeType: "S" }, - { AttributeName: "GSIPK_kid", AttributeType: "S" }, + { AttributeName: "GSIPK_clientId_kid", AttributeType: "S" }, { AttributeName: "GSIPK_clientId_purposeId", AttributeType: "S" }, ], KeySchema: [{ AttributeName: "PK", KeyType: "HASH" }], @@ -111,13 +111,13 @@ export const buildDynamoDBTables = async ( "consumerId", "clientKind", "publicKey", - "GSIPK_kid", + "GSIPK_clientId_kid", ], }, }, { - IndexName: "Kid", - KeySchema: [{ AttributeName: "GSIPK_kid", KeyType: "HASH" }], + IndexName: "ClientKid", + KeySchema: [{ AttributeName: "GSIPK_clientId_kid", KeyType: "HASH" }], Projection: { ProjectionType: "KEYS_ONLY", }, @@ -131,7 +131,7 @@ export const buildDynamoDBTables = async ( ProjectionType: "INCLUDE", NonKeyAttributes: [ "GSIPK_clientId", - "GSIPK_kid", + "GSIPK_clientId_kid", "GSIPK_purposeId", "consumerId", "clientKind", diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index edde65239a..da8f794901 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -61,7 +61,7 @@ import { GSIPKConsumerIdEServiceId, PlatformStatesAgreementEntry, PlatformStatesAgreementPK, - makeGSIPKKid, + makeGSIPKClientIdKid, TokenGenerationStatesClientKidPK, TokenGenerationStatesApiClient, makeTokenGenerationStatesClientKidPK, @@ -484,7 +484,7 @@ export const getMockTokenGenStatesConsumerClient = ( clientKind: clientKindTokenGenStates.consumer, publicKey: "PEM", GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId, kid }), agreementState: itemState.active, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ eserviceId, @@ -505,7 +505,7 @@ export const getMockTokenGenStatesConsumerClient = ( clientKind: clientKindTokenGenStates.consumer, publicKey: "PEM", GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId, kid }), }; } }; @@ -550,7 +550,7 @@ export const getMockTokenGenStatesApiClient = ( clientKind: clientKindTokenGenStates.api, publicKey: "PEM", GSIPK_clientId: clientId, - GSIPK_kid: makeGSIPKKid(kid), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId, kid }), }; }; diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index c8a91c0f2f..3ba49a0502 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -52,8 +52,8 @@ export const writeTokenGenStatesApiClient = async ( GSIPK_clientId: { S: tokenGenStatesEntry.GSIPK_clientId, }, - GSIPK_kid: { - S: tokenGenStatesEntry.GSIPK_kid, + GSIPK_clientId_kid: { + S: tokenGenStatesEntry.GSIPK_clientId_kid, }, }, TableName: "token-generation-states", @@ -131,8 +131,8 @@ export const writeTokenGenStatesConsumerClient = async ( GSIPK_clientId: { S: tokenGenStatesEntry.GSIPK_clientId, }, - GSIPK_kid: { - S: tokenGenStatesEntry.GSIPK_kid, + GSIPK_clientId_kid: { + S: tokenGenStatesEntry.GSIPK_clientId_kid, }, ...(tokenGenStatesEntry.GSIPK_clientId_purposeId ? { diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 3ea606295c..29204275fb 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -150,8 +150,8 @@ export type GSIPKEServiceIdDescriptorId = z.infer< export const GSIPKClientIdPurposeId = z.string().brand(`clientId#purposeId`); export type GSIPKClientIdPurposeId = z.infer; -export const GSIPKKid = z.string().brand("kid"); -export type GSIPKKid = z.infer; +export const GSIPKClientIdKid = z.string().brand("kid"); +export type GSIPKClientIdKid = z.infer; type IDS = | CorrelationId @@ -184,7 +184,7 @@ type IDS = | TokenGenerationStatesClientKidPK | GSIPKEServiceIdDescriptorId | GSIPKClientIdPurposeId - | GSIPKKid; + | GSIPKClientIdKid; // 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/token-generation-readmodel/commons.ts b/packages/models/src/token-generation-readmodel/commons.ts index 5c064660ff..67e42f85b0 100644 --- a/packages/models/src/token-generation-readmodel/commons.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -16,7 +16,7 @@ import { GSIPKEServiceIdDescriptorId, GSIPKClientIdPurposeId, unsafeBrandId, - GSIPKKid, + GSIPKClientIdKid, } from "../brandedIds.js"; export const makePlatformStatesEServiceDescriptorPK = ({ @@ -96,8 +96,13 @@ export const makeGSIPKClientIdPurposeId = ({ }): GSIPKClientIdPurposeId => unsafeBrandId(`${clientId}#${purposeId}`); -export const makeGSIPKKid = (kid: string): GSIPKKid => - unsafeBrandId(kid); +export const makeGSIPKClientIdKid = ({ + clientId, + kid, +}: { + clientId: ClientId; + kid: string; +}): GSIPKClientIdKid => unsafeBrandId(`${clientId}#${kid}`); export const clientKindTokenGenStates = { consumer: "CONSUMER", diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index d3663c5039..e8354e6b33 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -5,7 +5,7 @@ import { GSIPKClientIdPurposeId, GSIPKConsumerIdEServiceId, GSIPKEServiceIdDescriptorId, - GSIPKKid, + GSIPKClientIdKid, PurposeId, PurposeVersionId, TenantId, @@ -19,7 +19,7 @@ const TokenGenerationStatesBaseEntry = z.object({ consumerId: TenantId, publicKey: z.string(), GSIPK_clientId: ClientId, - GSIPK_kid: GSIPKKid, + GSIPK_clientId_kid: GSIPKClientIdKid, updatedAt: z.string().datetime(), }); type TokenGenerationStatesBaseEntry = z.infer< @@ -94,7 +94,7 @@ export const TokenGenStatesApiClientGSIClient = consumerId: true, clientKind: true, publicKey: true, - GSIPK_kid: true, + GSIPK_clientId_kid: true, }); export type TokenGenStatesApiClientGSIClient = z.infer< typeof TokenGenStatesApiClientGSIClient @@ -107,7 +107,7 @@ export const TokenGenStatesConsumerClientGSIClient = consumerId: true, clientKind: true, publicKey: true, - GSIPK_kid: true, + GSIPK_clientId_kid: true, }); export type TokenGenStatesConsumerClientGSIClient = z.infer< typeof TokenGenStatesConsumerClientGSIClient @@ -125,7 +125,7 @@ export const TokenGenStatesConsumerClientGSIClientPurpose = PK: true, GSIPK_clientId_purposeId: true, GSIPK_clientId: true, - GSIPK_kid: true, + GSIPK_clientId_kid: true, GSIPK_purposeId: true, consumerId: true, clientKind: true, @@ -145,29 +145,31 @@ export type TokenGenStatesConsumerClientGSIDescriptor = z.infer< typeof TokenGenStatesConsumerClientGSIDescriptor >; -// Kid -export const TokenGenStatesApiClientGSIKid = +// ClientKid +export const TokenGenStatesApiClientGSIClientKid = TokenGenerationStatesApiClient.pick({ PK: true, - GSIPK_kid: true, + GSIPK_clientId_kid: true, }); -export type TokenGenStatesApiClientGSIKid = z.infer< - typeof TokenGenStatesApiClientGSIKid +export type TokenGenStatesApiClientGSIClientIdKid = z.infer< + typeof TokenGenStatesApiClientGSIClientKid >; -export const TokenGenStatesConsumerClientGSIKid = +export const TokenGenStatesConsumerClientGSIClientKid = TokenGenerationStatesConsumerClient.pick({ PK: true, - GSIPK_kid: true, + GSIPK_clientId_kid: true, }); -export type TokenGenStatesConsumerClientGSIKid = z.infer< - typeof TokenGenStatesConsumerClientGSIKid +export type TokenGenStatesConsumerClientGSIClientKid = z.infer< + typeof TokenGenStatesConsumerClientGSIClientKid >; -export const TokenGenStatesGenericClientGSIKid = - TokenGenStatesApiClientGSIKid.or(TokenGenStatesConsumerClientGSIKid); -export type TokenGenStatesGenericClientGSIKid = z.infer< - typeof TokenGenStatesGenericClientGSIKid +export const TokenGenStatesGenericClientGSIClientKid = + TokenGenStatesApiClientGSIClientKid.or( + TokenGenStatesConsumerClientGSIClientKid + ); +export type TokenGenStatesGenericClientGSIClientKid = z.infer< + typeof TokenGenStatesGenericClientGSIClientKid >; // Purpose From 52561f95b6f81d928654f8d5006fbf355097f209 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 Jan 2025 12:19:21 +0100 Subject: [PATCH 100/126] Add logging in platformstate writers (#1353) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 65 ++++- .../src/consumerServiceV2.ts | 62 ++++- .../src/utils.ts | 25 +- .../consumerServiceV1.integration.test.ts | 120 +++++++-- .../consumerServiceV2.integration.test.ts | 198 +++++++++++--- .../test/utils.test.ts | 48 +++- .../src/consumerServiceV1.ts | 242 +++++++++++------- .../src/consumerServiceV2.ts | 42 ++- .../src/utils.ts | 60 +++-- .../consumerServiceV1.integration.test.ts | 60 +++-- .../consumerServiceV2.integration.test.ts | 54 ++-- .../test/utils.test.ts | 39 ++- .../src/consumerServiceV1.ts | 43 +++- .../src/consumerServiceV2.ts | 66 ++++- .../catalog-platformstate-writer/src/index.ts | 8 +- .../catalog-platformstate-writer/src/utils.ts | 55 +++- .../test/consumerServiceV1.test.ts | 45 +++- .../test/consumerServiceV2.test.ts | 83 ++++-- .../test/utils.test.ts | 41 ++- .../src/consumerServiceV1.ts | 19 +- .../src/consumerServiceV2.ts | 18 +- .../purpose-platformstate-writer/src/utils.ts | 17 +- .../consumerServiceV1.integration.test.ts | 21 +- .../consumerServiceV2.integration.test.ts | 57 +++-- .../test/utils.test.ts | 43 +++- 25 files changed, 1142 insertions(+), 389 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 46a412928e..eeba6cc8b7 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -62,14 +62,19 @@ export async function handleMessageV1( }) .with(agreementState.archived, async () => { const agreement = parseAgreement(msg.data.agreement); - await handleArchiving(agreement, dynamoDBClient); + await handleArchiving(agreement, dynamoDBClient, logger); }) .with( agreementState.draft, agreementState.missingCertifiedAttributes, agreementState.pending, agreementState.rejected, - () => Promise.resolve() + () => { + logger.info( + `Skipping processing of entry ${agreement.id}. Reason: state ${agreement.state}` + ); + return Promise.resolve(); + } ) .exhaustive(); }) @@ -89,7 +94,13 @@ export async function handleMessageV1( agreementState.missingCertifiedAttributes, agreementState.pending, agreementState.rejected, - () => Promise.resolve() + // eslint-disable-next-line sonarjs/no-identical-functions + () => { + logger.info( + `Skipping processing of entry ${agreement.id}. Reason: state ${agreement.state}` + ); + return Promise.resolve(); + } ) .exhaustive(); }) @@ -133,13 +144,17 @@ const handleActivationOrSuspension = async ( if (existingAgreementEntry) { if (existingAgreementEntry.version > incomingVersion) { // Stops processing if the message is older than the agreement entry + logger.info( + `Skipping processing of entry ${existingAgreementEntry}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), - incomingVersion + incomingVersion, + logger ); } } else { @@ -162,7 +177,7 @@ const handleActivationOrSuspension = async ( agreementDescriptorId: agreement.descriptorId, }; - await writeAgreementEntry(agreementEntry, dynamoDBClient); + await writeAgreementEntry(agreementEntry, dynamoDBClient, logger); } if ( @@ -193,12 +208,17 @@ const handleActivationOrSuspension = async ( catalogEntry, logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } }; const handleArchiving = async ( agreement: Agreement, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const primaryKey = makePlatformStatesAgreementPK(agreement.id); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ @@ -219,10 +239,15 @@ const handleArchiving = async ( GSIPK_consumerId_eserviceId, agreementState: agreement.state, dynamoDBClient, + logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } - await deleteAgreementEntry(primaryKey, dynamoDBClient); + await deleteAgreementEntry(primaryKey, dynamoDBClient, logger); }; const handleUpgrade = async ( @@ -247,13 +272,17 @@ const handleUpgrade = async ( if (agreementEntry) { if (agreementEntry.version > msgVersion) { + logger.info( + `Skipping processing of entry ${agreementEntry}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), - msgVersion + msgVersion, + logger ); } } else { @@ -278,11 +307,12 @@ const handleUpgrade = async ( agreementDescriptorId: agreement.descriptorId, }; - await writeAgreementEntry(newAgreementEntry, dynamoDBClient); + await writeAgreementEntry(newAgreementEntry, dynamoDBClient, logger); } const updateLatestAgreementOnTokenGenStates = async ( - catalogEntry: PlatformStatesCatalogEntry | undefined + catalogEntry: PlatformStatesCatalogEntry | undefined, + logger: Logger ): Promise => { if ( await isLatestAgreement( @@ -306,10 +336,14 @@ const handleUpgrade = async ( catalogEntry, logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } }; - await updateLatestAgreementOnTokenGenStates(catalogEntry); + await updateLatestAgreementOnTokenGenStates(catalogEntry, logger); const secondRetrievalCatalogEntry = await readCatalogEntry( pkCatalogEntry, @@ -319,6 +353,13 @@ const handleUpgrade = async ( secondRetrievalCatalogEntry && (!catalogEntry || secondRetrievalCatalogEntry.state !== catalogEntry.state) ) { - await updateLatestAgreementOnTokenGenStates(secondRetrievalCatalogEntry); + await updateLatestAgreementOnTokenGenStates( + secondRetrievalCatalogEntry, + logger + ); + } else { + logger.info( + `Token-generation-states. Second retrieval of catalog entry ${pkCatalogEntry} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` + ); } }; diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index 7a3b2433fd..abbf72fa2c 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -48,13 +48,17 @@ export async function handleMessageV2( if (existingAgreementEntry) { if (existingAgreementEntry.version > msg.version) { // Stops processing if the message is older than the agreement entry + logger.info( + `Skipping processing of entry ${existingAgreementEntry}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), - msg.version + msg.version, + logger ); } } else { @@ -74,7 +78,7 @@ export async function handleMessageV2( agreementDescriptorId: agreement.descriptorId, }; - await writeAgreementEntry(agreementEntry, dynamoDBClient); + await writeAgreementEntry(agreementEntry, dynamoDBClient, logger); } if ( @@ -108,6 +112,10 @@ export async function handleMessageV2( catalogEntry, logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } }) .with( @@ -126,13 +134,21 @@ export async function handleMessageV2( ); if (!agreementEntry || agreementEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${primaryKey}. Reason: ${ + !agreementEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); } else { await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), - msg.version + msg.version, + logger ); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ @@ -153,7 +169,12 @@ export async function handleMessageV2( GSIPK_consumerId_eserviceId, agreementState: agreement.state, dynamoDBClient, + logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } } } @@ -178,13 +199,17 @@ export async function handleMessageV2( if (agreementEntry) { if (agreementEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${agreementEntry}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), - msg.version + msg.version, + logger ); } } else { @@ -203,11 +228,12 @@ export async function handleMessageV2( agreementDescriptorId: agreement.descriptorId, }; - await writeAgreementEntry(newAgreementEntry, dynamoDBClient); + await writeAgreementEntry(newAgreementEntry, dynamoDBClient, logger); } const updateLatestAgreementOnTokenGenStates = async ( - catalogEntry: PlatformStatesCatalogEntry | undefined + catalogEntry: PlatformStatesCatalogEntry | undefined, + logger: Logger ): Promise => { if ( await isLatestAgreement( @@ -233,6 +259,10 @@ export async function handleMessageV2( catalogEntry, logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } }; @@ -241,7 +271,7 @@ export async function handleMessageV2( dynamoDBClient ); - await updateLatestAgreementOnTokenGenStates(catalogEntry); + await updateLatestAgreementOnTokenGenStates(catalogEntry, logger); const updatedCatalogEntry = await readCatalogEntry( pkCatalogEntry, @@ -252,13 +282,20 @@ export async function handleMessageV2( updatedCatalogEntry && (!catalogEntry || updatedCatalogEntry.state !== catalogEntry.state) ) { - await updateLatestAgreementOnTokenGenStates(updatedCatalogEntry); + await updateLatestAgreementOnTokenGenStates( + updatedCatalogEntry, + logger + ); + } else { + logger.info( + `Token-generation-states. Second retrieval of catalog entry ${pkCatalogEntry} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` + ); } }) .with({ type: "AgreementArchivedByUpgrade" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); const pk = makePlatformStatesAgreementPK(agreement.id); - await deleteAgreementEntry(pk, dynamoDBClient); + await deleteAgreementEntry(pk, dynamoDBClient, logger); }) .with({ type: "AgreementArchivedByConsumer" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); @@ -282,10 +319,15 @@ export async function handleMessageV2( GSIPK_consumerId_eserviceId, agreementState: agreement.state, dynamoDBClient, + logger, }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); } - await deleteAgreementEntry(primaryKey, dynamoDBClient); + await deleteAgreementEntry(primaryKey, dynamoDBClient, logger); }) .with( { type: "AgreementAdded" }, diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index cee6715880..9a03e5505a 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -37,7 +37,8 @@ import { config } from "./config/config.js"; export const writeAgreementEntry = async ( agreementEntry: PlatformStatesAgreementEntry, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", @@ -68,6 +69,7 @@ export const writeAgreementEntry = async ( }; const command = new PutItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Written agreement entry ${agreementEntry.PK}`); }; export const readAgreementEntry = async ( @@ -102,7 +104,8 @@ export const readAgreementEntry = async ( export const deleteAgreementEntry = async ( primaryKey: PlatformStatesAgreementPK, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: DeleteItemInput = { Key: { @@ -112,13 +115,15 @@ export const deleteAgreementEntry = async ( }; const command = new DeleteItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Deleted agreement entry ${primaryKey}`); }; export const updateAgreementStateInPlatformStatesEntry = async ( dynamoDBClient: DynamoDBClient, primaryKey: PlatformStatesAgreementPK, state: ItemState, - version: number + version: number, + logger: Logger ): Promise => { const input: UpdateItemInput = { ConditionExpression: "attribute_exists(PK)", @@ -148,6 +153,9 @@ export const updateAgreementStateInPlatformStatesEntry = async ( }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Platform-states. Updated agreement state in entry ${primaryKey}` + ); }; export const agreementStateToItemState = (state: AgreementState): ItemState => @@ -157,10 +165,12 @@ export const updateAgreementStateOnTokenGenStatesEntries = async ({ entriesToUpdate, agreementState, dynamoDBClient, + logger, }: { entriesToUpdate: TokenGenStatesConsumerClientGSIAgreement[]; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; + logger: Logger; }): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -186,6 +196,9 @@ export const updateAgreementStateOnTokenGenStatesEntries = async ({ }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Updated agreement state in entry ${entry.PK}` + ); } }; @@ -269,6 +282,9 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Updated agreement state and descriptor info in entry ${entry.PK}` + ); } }; @@ -445,10 +461,12 @@ export const updateAgreementStateOnTokenGenStates = async ({ GSIPK_consumerId_eserviceId, agreementState, dynamoDBClient, + logger, }: { GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId; agreementState: AgreementState; dynamoDBClient: DynamoDBClient; + logger: Logger; }): Promise => { const runPaginatedQuery = async ( consumerId_eserviceId: GSIPKConsumerIdEServiceId, @@ -492,6 +510,7 @@ export const updateAgreementStateOnTokenGenStates = async ({ entriesToUpdate: tokenGenStatesEntries.data, agreementState, dynamoDBClient, + logger, }); if (!data.LastEvaluatedKey) { diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 7a210628c1..a5cb1d4490 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -93,7 +93,11 @@ describe("integration tests V1 events", async () => { ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; - await writeAgreementEntry(previousStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -187,7 +191,11 @@ describe("integration tests V1 events", async () => { ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; - await writeAgreementEntry(previousStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -620,7 +628,11 @@ describe("integration tests V1 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); const catalogEntry: PlatformStatesCatalogEntry = { PK: primaryKeyCatalogEntry, @@ -741,7 +753,11 @@ describe("integration tests V1 events", async () => { ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 3, }; - await writeAgreementEntry(previousStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: agreement.eserviceId, @@ -890,8 +906,16 @@ describe("integration tests V1 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: latestAgreement.eserviceId, @@ -1066,8 +1090,16 @@ describe("integration tests V1 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: previousAgreement.eserviceId, @@ -1370,8 +1402,16 @@ describe("integration tests V1 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1509,8 +1549,16 @@ describe("integration tests V1 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1726,8 +1774,16 @@ describe("integration tests V1 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1865,8 +1921,16 @@ describe("integration tests V1 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -2030,8 +2094,16 @@ describe("integration tests V1 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -2172,8 +2244,16 @@ describe("integration tests V1 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index 51224f4722..117c7c2057 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -99,7 +99,11 @@ describe("integration tests V2 events", async () => { ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; - await writeAgreementEntry(previousStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -193,7 +197,11 @@ describe("integration tests V2 events", async () => { ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 2, }; - await writeAgreementEntry(previousStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -627,7 +635,11 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); const catalogEntry: PlatformStatesCatalogEntry = { PK: primaryKeyCatalogEntry, @@ -826,8 +838,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -965,8 +985,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1154,8 +1182,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1293,8 +1329,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1482,8 +1526,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1621,8 +1673,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1810,8 +1870,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1949,8 +2017,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -2139,8 +2215,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -2278,8 +2362,16 @@ describe("integration tests V2 events", async () => { latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -2390,7 +2482,11 @@ describe("integration tests V2 events", async () => { ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), version: 3, }; - await writeAgreementEntry(previousStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: agreement.eserviceId, @@ -2540,8 +2636,16 @@ describe("integration tests V2 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.upgrade!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: latestAgreement.eserviceId, @@ -2716,8 +2820,16 @@ describe("integration tests V2 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: previousAgreement.eserviceId, @@ -2993,7 +3105,11 @@ describe("integration tests V2 events", async () => { GSIPK_consumerId_eserviceId, }; - await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + agreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -3125,8 +3241,16 @@ describe("integration tests V2 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -3267,8 +3391,16 @@ describe("integration tests V2 events", async () => { GSISK_agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); - await writeAgreementEntry(latestAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index 9a52bb5520..6d4b5232e3 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -77,7 +77,8 @@ describe("utils", async () => { dynamoDBClient, primaryKey, itemState.active, - 1 + 1, + genericLogger ) ).rejects.toThrowError(ConditionalCheckFailedException); const agreementEntry = await readAgreementEntry( @@ -96,12 +97,17 @@ describe("utils", async () => { expect( await readAgreementEntry(primaryKey, dynamoDBClient) ).toBeUndefined(); - await writeAgreementEntry(previousAgreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, itemState.active, - 2 + 2, + genericLogger ); const result = await readAgreementEntry(primaryKey, dynamoDBClient); @@ -123,9 +129,13 @@ describe("utils", async () => { ); const agreementStateEntry = getMockPlatformStatesAgreementEntry(primaryKey); - await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + agreementStateEntry, + dynamoDBClient, + genericLogger + ); expect( - writeAgreementEntry(agreementStateEntry, dynamoDBClient) + writeAgreementEntry(agreementStateEntry, dynamoDBClient, genericLogger) ).rejects.toThrowError(ConditionalCheckFailedException); }); @@ -147,7 +157,11 @@ describe("utils", async () => { agreementDescriptorId: generateId(), }; - await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + agreementStateEntry, + dynamoDBClient, + genericLogger + ); const retrievedAgreementEntry = await readAgreementEntry( primaryKey, @@ -187,7 +201,11 @@ describe("utils", async () => { GSISK_agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }; - await writeAgreementEntry(agreementStateEntry, dynamoDBClient); + await writeAgreementEntry( + agreementStateEntry, + dynamoDBClient, + genericLogger + ); const retrievedEntry = await readAgreementEntry( primaryKey, dynamoDBClient @@ -203,7 +221,7 @@ describe("utils", async () => { generateId() ); expect( - deleteAgreementEntry(primaryKey, dynamoDBClient) + deleteAgreementEntry(primaryKey, dynamoDBClient, genericLogger) ).resolves.not.toThrowError(); }); @@ -224,8 +242,12 @@ describe("utils", async () => { GSISK_agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }; - await writeAgreementEntry(agreementStateEntry, dynamoDBClient); - await deleteAgreementEntry(primaryKey, dynamoDBClient); + await writeAgreementEntry( + agreementStateEntry, + dynamoDBClient, + genericLogger + ); + await deleteAgreementEntry(primaryKey, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient @@ -389,6 +411,7 @@ describe("utils", async () => { GSIPK_consumerId_eserviceId, agreementState: agreementState.archived, dynamoDBClient, + logger: genericLogger, }) ).resolves.not.toThrowError(); const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( @@ -441,6 +464,7 @@ describe("utils", async () => { GSIPK_consumerId_eserviceId, agreementState: agreementState.active, dynamoDBClient, + logger: genericLogger, }); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient @@ -665,8 +689,8 @@ describe("utils", async () => { state: itemState.inactive, }; - await writeAgreementEntry(agreementEntry1, dynamoDBClient); - await writeAgreementEntry(agreementEntry2, dynamoDBClient); + await writeAgreementEntry(agreementEntry1, dynamoDBClient, genericLogger); + await writeAgreementEntry(agreementEntry2, dynamoDBClient, genericLogger); expect( await isLatestAgreement( diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 4d87fcadad..d19fcd1909 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -60,6 +60,9 @@ export async function handleMessageV1( const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (clientEntry && clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${clientEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { const updatedClientEntry: PlatformStatesClientEntry = { @@ -71,7 +74,11 @@ export async function handleMessageV1( updatedAt: new Date().toISOString(), clientPurposesIds: client.purposes, }; - await upsertPlatformClientEntry(updatedClientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + updatedClientEntry, + dynamoDBClient, + logger + ); } }) // eslint-disable-next-line sonarjs/cognitive-complexity @@ -87,6 +94,13 @@ export async function handleMessageV1( const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (!clientEntry || clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${pk}. Reason: ${ + !clientEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); } else { const clientPurposesIds = clientEntry?.clientPurposesIds || []; @@ -100,7 +114,11 @@ export async function handleMessageV1( version: msg.version, updatedAt: new Date().toISOString(), }; - await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + platformClientEntry, + dynamoDBClient, + logger + ); await match(clientEntry.clientKind) .with(clientKindTokenGenStates.consumer, async () => { @@ -255,6 +273,13 @@ export async function handleMessageV1( const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (!clientEntry || clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${pk}. Reason: ${ + !clientEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); } else { const platformClientEntry: PlatformStatesClientEntry = { @@ -266,7 +291,11 @@ export async function handleMessageV1( version: msg.version, updatedAt: new Date().toISOString(), }; - await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + platformClientEntry, + dynamoDBClient, + logger + ); const GSIPK_clientId_kid = makeGSIPKClientIdKid({ clientId, @@ -292,47 +321,85 @@ export async function handleMessageV1( const pk = makePlatformStatesClientPK(clientId); const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (!clientEntry || clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${pk}. Reason: ${ + !clientEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); - } else { - const purposeIds = [...clientEntry.clientPurposesIds, purposeId]; - await setClientPurposeIdsInPlatformStatesEntry( - { - primaryKey: pk, - version: msg.version, - clientPurposeIds: purposeIds, - }, + } + + const purposeIds = [...clientEntry.clientPurposesIds, purposeId]; + await setClientPurposeIdsInPlatformStatesEntry( + { + primaryKey: pk, + version: msg.version, + clientPurposeIds: purposeIds, + }, + dynamoDBClient, + logger + ); + + const GSIPK_clientId = clientId; + const tokenGenStatesConsumerClients = + await readConsumerClientEntriesInTokenGenerationStates( + GSIPK_clientId, dynamoDBClient ); - - const GSIPK_clientId = clientId; - const tokenGenStatesConsumerClients = - await readConsumerClientEntriesInTokenGenerationStates( - GSIPK_clientId, - dynamoDBClient + if (tokenGenStatesConsumerClients.length === 0) { + logger.info( + `Skipping token-generation-states update. Reason: no entries found for GSIPK_clientId ${GSIPK_clientId}` + ); + return Promise.resolve(); + } else { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient, + logger ); - if (tokenGenStatesConsumerClients.length === 0) { - return Promise.resolve(); - } else { - const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient, - logger - ); - const seenKids = new Set(); - const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = - []; + const seenKids = new Set(); + const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = + []; + + for (const entry of tokenGenStatesConsumerClients) { + const addedTokenGenStatesConsumerClient = await match( + clientEntry.clientPurposesIds.length + ) + .with(0, async () => { + const newTokenGenStatesConsumerClient = + createTokenGenStatesConsumerClient({ + tokenGenStatesClient: entry, + kid: extractKidFromTokenGenStatesEntryPK(entry.PK), + clientId, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); - for (const entry of tokenGenStatesConsumerClients) { - const addedTokenGenStatesConsumerClient = await match( - clientEntry.clientPurposesIds.length - ) - .with(0, async () => { + await upsertTokenGenStatesConsumerClient( + newTokenGenStatesConsumerClient, + dynamoDBClient, + logger + ); + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient, + logger + ); + return newTokenGenStatesConsumerClient; + }) + .with(P.number.gt(0), async () => { + const kid = extractKidFromTokenGenStatesEntryPK(entry.PK); + if (!seenKids.has(kid)) { const newTokenGenStatesConsumerClient = createTokenGenStatesConsumerClient({ tokenGenStatesClient: entry, - kid: extractKidFromTokenGenStatesEntryPK(entry.PK), + kid, clientId, purposeId, purposeEntry, @@ -345,71 +412,44 @@ export async function handleMessageV1( dynamoDBClient, logger ); - await deleteClientEntryFromTokenGenerationStates( - entry.PK, - dynamoDBClient, - logger - ); + seenKids.add(kid); return newTokenGenStatesConsumerClient; - }) - .with(P.number.gt(0), async () => { - const kid = extractKidFromTokenGenStatesEntryPK(entry.PK); - if (!seenKids.has(kid)) { - const newTokenGenStatesConsumerClient = - createTokenGenStatesConsumerClient({ - tokenGenStatesClient: entry, - kid, - clientId, - purposeId, - purposeEntry, - agreementEntry, - catalogEntry, - }); + } + return null; + }) + .run(); - await upsertTokenGenStatesConsumerClient( - newTokenGenStatesConsumerClient, - dynamoDBClient, - logger - ); - seenKids.add(kid); - return newTokenGenStatesConsumerClient; - } - return null; - }) - .run(); - - if (addedTokenGenStatesConsumerClient) { - // eslint-disable-next-line functional/immutable-data - addedTokenGenStatesConsumerClients.push( - addedTokenGenStatesConsumerClient - ); - } + if (addedTokenGenStatesConsumerClient) { + // eslint-disable-next-line functional/immutable-data + addedTokenGenStatesConsumerClients.push( + addedTokenGenStatesConsumerClient + ); } + } - // Second check for updated fields - await Promise.all( - addedTokenGenStatesConsumerClients.map(async (entry) => { - const { - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - } = await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient, - logger - ); + // Second check for updated fields + await Promise.all( + addedTokenGenStatesConsumerClients.map(async (entry) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient, + logger + ); - await updateTokenGenStatesDataForSecondRetrieval({ - dynamoDBClient, - entry, - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - logger, - }); - }) - ); - } + await updateTokenGenStatesDataForSecondRetrieval({ + dynamoDBClient, + entry, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + logger, + }); + }) + ); } }) .with({ type: "ClientPurposeRemoved" }, async (msg) => { @@ -421,6 +461,9 @@ export async function handleMessageV1( if (clientEntry) { if (clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${pk}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { const updatedPurposeIds = clientEntry.clientPurposesIds.filter( @@ -438,7 +481,8 @@ export async function handleMessageV1( version: msg.version, clientPurposeIds: updatedPurposeIds, }, - dynamoDBClient + dynamoDBClient, + logger ); // token-generation-states @@ -456,12 +500,16 @@ export async function handleMessageV1( ); } } + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry ${pk}. Reason: not found in platform-states` + ); } }) .with({ type: "ClientDeleted" }, async (msg) => { const clientId = unsafeBrandId(msg.data.clientId); const pk = makePlatformStatesClientPK(clientId); - await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); + await deleteClientEntryFromPlatformStates(pk, dynamoDBClient, logger); const GSIPK_clientId = clientId; await deleteEntriesFromTokenGenStatesByClientId( diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 1db8722148..c629be58eb 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -68,6 +68,9 @@ export async function handleMessageV2( ); if (clientEntry && clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${clientEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { // platform-states @@ -80,7 +83,11 @@ export async function handleMessageV2( version: msg.version, updatedAt: new Date().toISOString(), }; - await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + platformClientEntry, + dynamoDBClient, + logger + ); } // token-generation-states @@ -241,6 +248,9 @@ export async function handleMessageV2( const clientEntry = await readPlatformClientEntry(pk, dynamoDBClient); if (clientEntry && clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${clientEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { const platformClientEntry: PlatformStatesClientEntry = { @@ -252,7 +262,11 @@ export async function handleMessageV2( version: msg.version, updatedAt: new Date().toISOString(), }; - await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + platformClientEntry, + dynamoDBClient, + logger + ); } const GSIPK_clientId_kid = makeGSIPKClientIdKid({ @@ -275,6 +289,9 @@ export async function handleMessageV2( dynamoDBClient ); if (clientEntry && clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${clientEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { // platform-states @@ -287,7 +304,11 @@ export async function handleMessageV2( version: msg.version, updatedAt: new Date().toISOString(), }; - await upsertPlatformClientEntry(platformClientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + platformClientEntry, + dynamoDBClient, + logger + ); } // token-generation-states @@ -298,6 +319,9 @@ export async function handleMessageV2( dynamoDBClient ); if (tokenGenStatesConsumerClients.length === 0) { + logger.info( + `Skipping token-generation-states update. Reason: no entries found for GSIPK_clientId ${GSIPK_clientId}` + ); return Promise.resolve(); } else { const purposeId = unsafeBrandId(msg.data.purposeId); @@ -406,6 +430,9 @@ export async function handleMessageV2( if (clientEntry) { if (clientEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${clientEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ @@ -416,7 +443,8 @@ export async function handleMessageV2( // platform-states await setClientPurposeIdsInPlatformStatesEntry( { primaryKey: pk, version: msg.version, clientPurposeIds: [] }, - dynamoDBClient + dynamoDBClient, + logger ); // token-generation-states @@ -434,12 +462,16 @@ export async function handleMessageV2( ); } } + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry ${pk}. Reason: entry not found in platform-states` + ); } }) .with({ type: "ClientDeleted" }, async (msg) => { const client = parseClient(msg.data.client, msg.type); const pk = makePlatformStatesClientPK(client.id); - await deleteClientEntryFromPlatformStates(pk, dynamoDBClient); + await deleteClientEntryFromPlatformStates(pk, dynamoDBClient, logger); const GSIPK_clientId = client.id; await deleteEntriesFromTokenGenStatesByClientId( diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index b026892967..803396bb99 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -121,7 +121,8 @@ export const deleteEntriesFromTokenGenStatesByClientIdKid = async ( export const deleteClientEntryFromPlatformStates = async ( pk: PlatformStatesClientPK, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: DeleteItemInput = { Key: { @@ -131,6 +132,7 @@ export const deleteClientEntryFromPlatformStates = async ( }; const command = new DeleteItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Deleted client entry ${pk}`); }; export const deleteEntriesFromTokenGenStatesByClientId = async ( @@ -211,11 +213,8 @@ export const deleteClientEntryFromTokenGenerationStates = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new DeleteItemCommand(input); - logger.warn( - `Deleting entry ${entryToDeletePK}. Result ${JSON.stringify( - await dynamoDBClient.send(command) - )}` - ); + await dynamoDBClient.send(command); + logger.info(`Token-generation-states. Deleted entry ${entryToDeletePK}`); }; export const readPlatformClientEntry = async ( @@ -421,10 +420,9 @@ export const writeTokenGenStatesApiClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - logger.warn( - `Writing api client ${tokenGenStatesApiClient.PK}. Result ${JSON.stringify( - await dynamoDBClient.send(command) - )}` + await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Written api client ${tokenGenStatesApiClient.PK}` ); }; @@ -637,10 +635,9 @@ export const upsertTokenGenStatesConsumerClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - logger.warn( - `Upserting consumer client ${ - tokenGenStatesConsumerClient.PK - }. Result ${JSON.stringify(await dynamoDBClient.send(command))}` + await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Upserted consumer client ${tokenGenStatesConsumerClient.PK}` ); }; @@ -758,10 +755,9 @@ export const writeTokenGenStatesConsumerClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - logger.warn( - `Writing consumer client ${ - tokenGenStatesConsumerClient.PK - }. Result ${JSON.stringify(await dynamoDBClient.send(command))}` + await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Written consumer client ${tokenGenStatesConsumerClient.PK}` ); }; @@ -775,7 +771,8 @@ export const clientKindToTokenGenerationStatesClientKind = ( export const writePlatformClientEntry = async ( clientEntry: PlatformStatesClientEntry, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", @@ -808,6 +805,7 @@ export const writePlatformClientEntry = async ( }; const command = new PutItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Written client entry ${clientEntry.PK}`); }; export const readConsumerClientEntriesInTokenGenerationStates = async ( @@ -881,7 +879,8 @@ export const setClientPurposeIdsInPlatformStatesEntry = async ( version: number; clientPurposeIds: PurposeId[]; }, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: UpdateItemInput = { ConditionExpression: "attribute_exists(PK)", @@ -910,6 +909,9 @@ export const setClientPurposeIdsInPlatformStatesEntry = async ( }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Platform-states. Updated purpose ids in client entry ${primaryKey}` + ); }; export const extractKidFromTokenGenStatesEntryPK = ( @@ -1005,7 +1007,8 @@ export const retrievePlatformStatesByPurpose = async ( export const upsertPlatformClientEntry = async ( entry: PlatformStatesClientEntry, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { Item: { @@ -1037,6 +1040,7 @@ export const upsertPlatformClientEntry = async ( }; const command = new PutItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Upserted client entry ${entry.PK}`); }; export const upsertTokenGenStatesApiClient = async ( @@ -1071,11 +1075,8 @@ export const upsertTokenGenStatesApiClient = async ( TableName: config.tokenGenerationReadModelTableNameTokenGeneration, }; const command = new PutItemCommand(input); - logger.warn( - `Upserting api client ${entry.PK}. Result ${JSON.stringify( - await dynamoDBClient.send(command) - )}` - ); + await dynamoDBClient.send(command); + logger.info(`Token-generation-states. Upserted api client ${entry.PK}`); }; export const updateTokenGenStatesDataForSecondRetrieval = async ({ @@ -1165,11 +1166,8 @@ export const updateTokenGenStatesDataForSecondRetrieval = async ({ ReturnValues: "NONE", }; const command = new UpdateItemCommand(input); - logger.warn( - `Updating entry ${entry.PK}. Result ${JSON.stringify( - await dynamoDBClient.send(command) - )}` - ); + await dynamoDBClient.send(command); + logger.info(`Token-generation-states. Updated entry ${entry.PK}`); } }; diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index f4769325a2..50ca61fe6b 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -124,7 +124,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -212,7 +213,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -278,7 +280,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -532,7 +535,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -831,7 +835,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1079,7 +1084,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1185,7 +1191,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1306,7 +1313,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1467,7 +1475,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1579,7 +1588,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ @@ -1724,7 +1734,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1865,7 +1876,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2137,7 +2149,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2422,7 +2435,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2639,7 +2653,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2771,7 +2786,8 @@ describe("integration tests V1 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2906,8 +2922,16 @@ describe("integration tests V1 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writePlatformClientEntry(clientPlatformStateEntry1, dynamoDBClient); - await writePlatformClientEntry(clientPlatformStateEntry2, dynamoDBClient); + await writePlatformClientEntry( + clientPlatformStateEntry1, + dynamoDBClient, + genericLogger + ); + await writePlatformClientEntry( + clientPlatformStateEntry2, + dynamoDBClient, + genericLogger + ); // token-generation-states const pkTokenGenStates1 = makeTokenGenerationStatesClientKidPurposePK({ diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index c3362c18d3..fa4ea1c986 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -127,7 +127,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -597,7 +598,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -894,7 +896,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1232,7 +1235,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1334,7 +1338,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1455,7 +1460,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1615,7 +1621,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1721,7 +1728,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ @@ -1792,7 +1800,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -1924,7 +1933,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2188,7 +2198,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2466,7 +2477,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2683,7 +2695,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2816,7 +2829,8 @@ describe("integration tests V2 events", async () => { }; await writePlatformClientEntry( previousPlatformClientEntry, - dynamoDBClient + dynamoDBClient, + genericLogger ); // token-generation-states @@ -2952,8 +2966,16 @@ describe("integration tests V2 events", async () => { updatedAt: new Date().toISOString(), clientPurposesIds: [], }; - await writePlatformClientEntry(clientPlatformStateEntry1, dynamoDBClient); - await writePlatformClientEntry(clientPlatformStateEntry2, dynamoDBClient); + await writePlatformClientEntry( + clientPlatformStateEntry1, + dynamoDBClient, + genericLogger + ); + await writePlatformClientEntry( + clientPlatformStateEntry2, + dynamoDBClient, + genericLogger + ); // token-generation-states const pkTokenGenStates1 = makeTokenGenerationStatesClientKidPurposePK({ diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 8ca055d2be..ac7c1a56be 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -159,10 +159,14 @@ describe("utils", () => { ...getMockPlatformStatesClientEntry(pk2), }; - await writePlatformClientEntry(clientEntry1, dynamoDBClient); - await writePlatformClientEntry(clientEntry2, dynamoDBClient); + await writePlatformClientEntry(clientEntry1, dynamoDBClient, genericLogger); + await writePlatformClientEntry(clientEntry2, dynamoDBClient, genericLogger); - await deleteClientEntryFromPlatformStates(pk1, dynamoDBClient); + await deleteClientEntryFromPlatformStates( + pk1, + dynamoDBClient, + genericLogger + ); const res = await readAllPlatformStatesItems(dynamoDBClient); @@ -275,8 +279,8 @@ describe("utils", () => { const clientEntry2 = getMockPlatformStatesClientEntry(); - await writePlatformClientEntry(clientEntry1, dynamoDBClient); - await writePlatformClientEntry(clientEntry2, dynamoDBClient); + await writePlatformClientEntry(clientEntry1, dynamoDBClient, genericLogger); + await writePlatformClientEntry(clientEntry2, dynamoDBClient, genericLogger); const res = await readPlatformClientEntry(clientEntry1.PK, dynamoDBClient); @@ -589,8 +593,8 @@ describe("utils", () => { clientPurposesIds: [generateId(), generateId()], }; - await writePlatformClientEntry(clientEntry1, dynamoDBClient); - await writePlatformClientEntry(clientEntry2, dynamoDBClient); + await writePlatformClientEntry(clientEntry1, dynamoDBClient, genericLogger); + await writePlatformClientEntry(clientEntry2, dynamoDBClient, genericLogger); await setClientPurposeIdsInPlatformStatesEntry( { @@ -598,7 +602,8 @@ describe("utils", () => { version: clientEntry1.version + 1, clientPurposeIds: [], }, - dynamoDBClient + dynamoDBClient, + genericLogger ); const res = await readAllPlatformStatesItems(dynamoDBClient); @@ -684,7 +689,11 @@ describe("utils", () => { const resultBefore = await readAllPlatformStatesItems(dynamoDBClient); expect(resultBefore).toEqual([]); - await upsertPlatformClientEntry(clientEntry, dynamoDBClient); + await upsertPlatformClientEntry( + clientEntry, + dynamoDBClient, + genericLogger + ); const resultAfter = await readAllPlatformStatesItems(dynamoDBClient); expect(resultAfter).toEqual([clientEntry]); @@ -695,13 +704,21 @@ describe("utils", () => { clientKind: clientKindTokenGenStates.consumer, }; - await writePlatformClientEntry(clientEntry, dynamoDBClient); + await writePlatformClientEntry( + clientEntry, + dynamoDBClient, + genericLogger + ); const updatedEntry: PlatformStatesClientEntry = { ...clientEntry, clientKind: clientKindTokenGenStates.api, }; - await upsertPlatformClientEntry(updatedEntry, dynamoDBClient); + await upsertPlatformClientEntry( + updatedEntry, + dynamoDBClient, + genericLogger + ); const resultAfter = await readAllPlatformStatesItems(dynamoDBClient); expect(resultAfter).toEqual([updatedEntry]); diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts index f75bc87ed8..25ef6d69ec 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts @@ -13,6 +13,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { Logger } from "pagopa-interop-commons"; import { deleteCatalogEntry, descriptorStateToItemState, @@ -25,7 +26,8 @@ import { export async function handleMessageV1( message: EServiceEventEnvelopeV1, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with({ type: "EServiceDescriptorUpdated" }, async (msg) => { @@ -46,6 +48,9 @@ export async function handleMessageV1( if (existingCatalogEntry) { if (existingCatalogEntry.version > msg.version) { // Stops processing if the message is older than the catalog entry + logger.info( + `Skipping processing of entry ${existingCatalogEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { // suspended->published @@ -54,7 +59,8 @@ export async function handleMessageV1( dynamoDBClient, eserviceDescriptorPK, descriptorStateToItemState(descriptor.state), - msg.version + msg.version, + logger ); } } else { @@ -69,7 +75,7 @@ export async function handleMessageV1( updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writeCatalogEntry(catalogEntry, dynamoDBClient, logger); } // token-generation-states @@ -82,7 +88,8 @@ export async function handleMessageV1( descriptorStateToItemState(descriptor.state), descriptor.voucherLifespan, descriptor.audience, - dynamoDBClient + dynamoDBClient, + logger ); }) .with(descriptorState.suspended, async () => { @@ -95,6 +102,15 @@ export async function handleMessageV1( !existingCatalogEntry || existingCatalogEntry.version > msg.version ) { + logger.info( + `Skipping processing of entry ${ + existingCatalogEntry?.PK + }. Reason: ${ + !existingCatalogEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); } else { // platform-states @@ -102,7 +118,8 @@ export async function handleMessageV1( dynamoDBClient, eserviceDescriptorPK, descriptorStateToItemState(descriptor.state), - msg.version + msg.version, + logger ); // token-generation-states @@ -113,7 +130,8 @@ export async function handleMessageV1( await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, descriptorStateToItemState(descriptor.state), - dynamoDBClient + dynamoDBClient, + logger ); } }) @@ -129,7 +147,7 @@ export async function handleMessageV1( eserviceId, descriptorId: descriptor.id, }); - await deleteCatalogEntry(primaryKey, dynamoDBClient); + await deleteCatalogEntry(primaryKey, dynamoDBClient, logger); // token-generation-states const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ @@ -139,14 +157,21 @@ export async function handleMessageV1( await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, descriptorStateToItemState(descriptor.state), - dynamoDBClient + dynamoDBClient, + logger ); }) .with( descriptorState.draft, descriptorState.deprecated, descriptorState.waitingForApproval, - () => Promise.resolve() + () => { + logger.info( + `Skipping processing of entry ${eserviceDescriptorPK}. Reason: state ${descriptor.state}` + ); + + return Promise.resolve(); + } ) .exhaustive(); }) diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index cf3a5b569d..1a057b7b85 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -14,6 +14,7 @@ import { unsafeBrandId, } from "pagopa-interop-models"; import { match } from "ts-pattern"; +import { Logger } from "pagopa-interop-commons"; import { deleteCatalogEntry, descriptorStateToItemState, @@ -28,7 +29,8 @@ import { export async function handleMessageV2( message: EServiceEventEnvelopeV2, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise { await match(message) .with( @@ -57,13 +59,17 @@ export async function handleMessageV2( if (existingCatalogEntryCurrent) { if (existingCatalogEntryCurrent.version > msg.version) { // Stops processing if the message is older than the catalog entry + logger.info( + `Skipping processing of entry ${existingCatalogEntryCurrent.PK} (the current descriptor). Reason: it already exists` + ); return Promise.resolve(); } else { await updateDescriptorStateInPlatformStatesEntry( dynamoDBClient, primaryKeyCurrent, descriptorStateToItemState(descriptor.state), - msg.version + msg.version, + logger ); } } else { @@ -76,7 +82,7 @@ export async function handleMessageV2( updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writeCatalogEntry(catalogEntry, dynamoDBClient, logger); } // token-generation-states @@ -89,7 +95,8 @@ export async function handleMessageV2( descriptorStateToItemState(descriptor.state), descriptor.voucherLifespan, descriptor.audience, - dynamoDBClient + dynamoDBClient, + logger ); }; @@ -101,6 +108,15 @@ export async function handleMessageV2( !previousDescriptor || previousDescriptor.state !== descriptorState.archived ) { + logger.info( + `Skipping processing of descriptor ${ + previousDescriptor?.id + } (the previous descriptor). Reason: ${ + !previousDescriptor + ? "entry doesn't exists" + : "state is not archived" + }` + ); return Promise.resolve(); } else { const primaryKeyPrevious = makePlatformStatesEServiceDescriptorPK({ @@ -108,7 +124,7 @@ export async function handleMessageV2( descriptorId: previousDescriptor.id, }); - await deleteCatalogEntry(primaryKeyPrevious, dynamoDBClient); + await deleteCatalogEntry(primaryKeyPrevious, dynamoDBClient, logger); // token-generation-states const eserviceId_descriptorId_previous = @@ -119,7 +135,8 @@ export async function handleMessageV2( await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId_previous, descriptorStateToItemState(previousDescriptor.state), - dynamoDBClient + dynamoDBClient, + logger ); } } @@ -140,13 +157,22 @@ export async function handleMessageV2( const catalogEntry = await readCatalogEntry(primaryKey, dynamoDBClient); if (!catalogEntry || catalogEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${catalogEntry?.PK}. Reason: ${ + !catalogEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); + return Promise.resolve(); } else { await updateDescriptorStateInPlatformStatesEntry( dynamoDBClient, primaryKey, descriptorStateToItemState(descriptor.state), - msg.version + msg.version, + logger ); // token-generation-states @@ -157,7 +183,8 @@ export async function handleMessageV2( await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, descriptorStateToItemState(descriptor.state), - dynamoDBClient + dynamoDBClient, + logger ); } } @@ -173,7 +200,7 @@ export async function handleMessageV2( eserviceId: eservice.id, descriptorId: unsafeBrandId(msg.data.descriptorId), }); - await deleteCatalogEntry(primaryKey, dynamoDBClient); + await deleteCatalogEntry(primaryKey, dynamoDBClient, logger); // token-generation-states const descriptorId = unsafeBrandId(msg.data.descriptorId); @@ -184,7 +211,8 @@ export async function handleMessageV2( await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, descriptorStateToItemState(descriptor.state), - dynamoDBClient + dynamoDBClient, + logger ); }) .with({ type: "EServiceDescriptorQuotasUpdated" }, async (msg) => { @@ -200,6 +228,14 @@ export async function handleMessageV2( const catalogEntry = await readCatalogEntry(primaryKey, dynamoDBClient); if (!catalogEntry || catalogEntry.version > msg.version) { + logger.info( + `Skipping processing of entry ${catalogEntry?.PK}. Reason: ${ + !catalogEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); + return Promise.resolve(); } else { if ( @@ -209,7 +245,8 @@ export async function handleMessageV2( dynamoDBClient, primaryKey, descriptor.voucherLifespan, - msg.version + msg.version, + logger ); // token-generation-states @@ -220,7 +257,12 @@ export async function handleMessageV2( await updateDescriptorVoucherLifespanInTokenGenerationStatesTable( eserviceId_descriptorId, descriptor.voucherLifespan, - dynamoDBClient + dynamoDBClient, + logger + ); + } else { + logger.info( + `Platform-states and Token-generation-states. Skipping processing of entry ${primaryKey}. Reason: unchanged voucherLifespan` ); } } diff --git a/packages/catalog-platformstate-writer/src/index.ts b/packages/catalog-platformstate-writer/src/index.ts index 8a7dca674f..38b0a9aaea 100644 --- a/packages/catalog-platformstate-writer/src/index.ts +++ b/packages/catalog-platformstate-writer/src/index.ts @@ -32,8 +32,12 @@ async function processMessage({ }); await match(decodedMessage) - .with({ event_version: 1 }, (msg) => handleMessageV1(msg, dynamoDBClient)) - .with({ event_version: 2 }, (msg) => handleMessageV2(msg, dynamoDBClient)) + .with({ event_version: 1 }, (msg) => + handleMessageV1(msg, dynamoDBClient, loggerInstance) + ) + .with({ event_version: 2 }, (msg) => + handleMessageV2(msg, dynamoDBClient, loggerInstance) + ) .exhaustive(); loggerInstance.info( diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index d291c57c80..bc0f992d47 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -27,11 +27,13 @@ import { } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { z } from "zod"; +import { Logger } from "pagopa-interop-commons"; import { config } from "./config/config.js"; export const writeCatalogEntry = async ( catalogEntry: PlatformStatesCatalogEntry, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", @@ -61,6 +63,7 @@ export const writeCatalogEntry = async ( }; const command = new PutItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Written catalog entry ${catalogEntry.PK}`); }; export const readCatalogEntry = async ( @@ -95,7 +98,8 @@ export const readCatalogEntry = async ( export const deleteCatalogEntry = async ( primaryKey: PlatformStatesEServiceDescriptorPK, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const input: DeleteItemInput = { Key: { @@ -105,6 +109,7 @@ export const deleteCatalogEntry = async ( }; const command = new DeleteItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Deleted catalog entry ${primaryKey}`); }; export const descriptorStateToItemState = (state: DescriptorState): ItemState => @@ -116,7 +121,8 @@ export const updateDescriptorStateInPlatformStatesEntry = async ( dynamoDBClient: DynamoDBClient, primaryKey: PlatformStatesEServiceDescriptorPK, state: ItemState, - version: number + version: number, + logger: Logger ): Promise => { const input: UpdateItemInput = { ConditionExpression: "attribute_exists(PK)", @@ -146,13 +152,17 @@ export const updateDescriptorStateInPlatformStatesEntry = async ( }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Platform-states. Updated descriptor state in entry ${primaryKey}` + ); }; export const updateDescriptorVoucherLifespanInPlatformStateEntry = async ( dynamoDBClient: DynamoDBClient, primaryKey: PlatformStatesEServiceDescriptorPK, voucherLifespan: number, - version: number + version: number, + logger: Logger ): Promise => { const input: UpdateItemInput = { ConditionExpression: "attribute_exists(PK)", @@ -179,12 +189,16 @@ export const updateDescriptorVoucherLifespanInPlatformStateEntry = async ( }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Platform-states. Updated descriptor voucher lifespan state in entry ${primaryKey}` + ); }; export const updateDescriptorStateInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, descriptorState: ItemState, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, @@ -227,7 +241,8 @@ export const updateDescriptorStateInTokenGenerationStatesTable = async ( await updateDescriptorStateInTokenGenerationStatesEntries( descriptorState, dynamoDBClient, - tokenGenStatesEntries.data + tokenGenStatesEntries.data, + logger ); if (!data.LastEvaluatedKey) { @@ -257,7 +272,9 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( descriptorState: ItemState, descriptorVoucherLifespan: number, descriptorAudience: string[], - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger + // eslint-disable-next-line max-params ): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, @@ -303,6 +320,7 @@ export const updateDescriptorInfoInTokenGenerationStatesTable = async ( descriptorAudience, dynamoDBClient, entriesToUpdate: tokenGenStatesEntries.data, + logger, }); if (!data.LastEvaluatedKey) { @@ -331,7 +349,8 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, voucherLifespan: number, - dynamoDBClient: DynamoDBClient + dynamoDBClient: DynamoDBClient, + logger: Logger ): Promise => { const runPaginatedQuery = async ( eserviceId_descriptorId: GSIPKEServiceIdDescriptorId, @@ -374,7 +393,8 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = await updateDescriptorVoucherLifespanInTokenGenerationStatesEntries( voucherLifespan, dynamoDBClient, - tokenGenStatesEntries.data + tokenGenStatesEntries.data, + logger ); if (data.LastEvaluatedKey) { @@ -393,7 +413,8 @@ export const updateDescriptorVoucherLifespanInTokenGenerationStatesTable = const updateDescriptorStateInTokenGenerationStatesEntries = async ( descriptorState: ItemState, dynamoDBClient: DynamoDBClient, - entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[] + entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[], + logger: Logger ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -418,6 +439,9 @@ const updateDescriptorStateInTokenGenerationStatesEntries = async ( }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Updated descriptor state in entry ${entry.PK}` + ); } }; @@ -427,12 +451,14 @@ const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ descriptorAudience, dynamoDBClient, entriesToUpdate, + logger, }: { descriptorState: ItemState; descriptorVoucherLifespan: number; descriptorAudience: string[]; dynamoDBClient: DynamoDBClient; entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[]; + logger: Logger; }): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -465,13 +491,17 @@ const updateDescriptorInfoInTokenGenerationStatesEntries = async ({ }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Updated descriptor info in entry ${entry.PK}` + ); } }; const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( voucherLifespan: number, dynamoDBClient: DynamoDBClient, - entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[] + entriesToUpdate: TokenGenStatesConsumerClientGSIDescriptor[], + logger: Logger ): Promise => { for (const entry of entriesToUpdate) { const input: UpdateItemInput = { @@ -496,5 +526,8 @@ const updateDescriptorVoucherLifespanInTokenGenerationStatesEntries = async ( }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Updated descriptor info in entry ${entry.PK}` + ); } }; diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts index 4cdee805f0..244cb7a529 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV1.test.ts @@ -34,6 +34,7 @@ import { readAllTokenGenStatesItems, } from "pagopa-interop-commons-test"; import { writeTokenGenStatesConsumerClient } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { dynamoDBClient } from "./utils.js"; @@ -124,7 +125,7 @@ describe("V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const primaryKey = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, @@ -206,7 +207,11 @@ describe("V1 events", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -249,7 +254,7 @@ describe("V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedCatalogEntry = await readCatalogEntry( primaryKey, @@ -325,7 +330,11 @@ describe("V1 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousCatalogStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousCatalogStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -368,7 +377,7 @@ describe("V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedCatalogEntry = await readCatalogEntry( catalogPrimaryKey, @@ -429,7 +438,11 @@ describe("V1 events", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -471,7 +484,7 @@ describe("V1 events", async () => { tokenGenStatesConsumerClient2, dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -547,7 +560,11 @@ describe("V1 events", async () => { updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -589,7 +606,7 @@ describe("V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -637,7 +654,7 @@ describe("V1 events", async () => { log_date: new Date(), }; - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const primaryKey = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, @@ -692,7 +709,11 @@ describe("V1 events", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -735,7 +756,7 @@ describe("V1 events", async () => { dynamoDBClient ); - await handleMessageV1(message, dynamoDBClient); + await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); expect(retrievedEntry).toBeUndefined(); diff --git a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts index ae9441daa5..b3889db20e 100644 --- a/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts +++ b/packages/catalog-platformstate-writer/test/consumerServiceV2.test.ts @@ -38,6 +38,7 @@ import { readAllTokenGenStatesItems, writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; +import { genericLogger } from "pagopa-interop-commons"; import { readCatalogEntry, writeCatalogEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { dynamoDBClient } from "./utils.js"; @@ -96,7 +97,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -139,7 +144,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedCatalogEntry = await readCatalogEntry( @@ -200,7 +205,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -243,7 +252,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states const retrievedCatalogEntry = await readCatalogEntry( @@ -356,7 +365,7 @@ describe("integration tests V2 events", async () => { ); expect( - handleMessageV2(message, dynamoDBClient) + handleMessageV2(message, dynamoDBClient, genericLogger) ).resolves.not.toThrowError(); // platform-states @@ -421,7 +430,11 @@ describe("integration tests V2 events", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, @@ -463,7 +476,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); expect(retrievedEntry).toBeUndefined(); @@ -563,7 +576,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const primaryKey = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, @@ -646,7 +659,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -688,7 +705,7 @@ describe("integration tests V2 events", async () => { tokenGenStatesConsumerClient2, dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry( primaryKey, @@ -746,7 +763,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); const eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ eserviceId: eservice.id, descriptorId: publishedDescriptor.id, @@ -787,7 +808,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -893,7 +914,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const primaryKey = makePlatformStatesEServiceDescriptorPK({ eserviceId: eservice.id, @@ -971,7 +992,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1014,7 +1039,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); @@ -1072,7 +1097,11 @@ describe("integration tests V2 events", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1115,7 +1144,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); const expectedEntry: PlatformStatesCatalogEntry = { @@ -1225,7 +1254,7 @@ describe("integration tests V2 events", async () => { ); expect( - handleMessageV2(message, dynamoDBClient) + handleMessageV2(message, dynamoDBClient, genericLogger) ).resolves.not.toThrowError(); // platform-states @@ -1301,7 +1330,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1346,7 +1379,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); @@ -1415,7 +1448,11 @@ describe("integration tests V2 events", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousStateEntry, + dynamoDBClient, + genericLogger + ); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1460,7 +1497,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - await handleMessageV2(message, dynamoDBClient); + await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readCatalogEntry(primaryKey, dynamoDBClient); const expectedEntry: PlatformStatesCatalogEntry = { @@ -1583,7 +1620,7 @@ describe("integration tests V2 events", async () => { ); expect( - handleMessageV2(message, dynamoDBClient) + handleMessageV2(message, dynamoDBClient, genericLogger) ).resolves.not.toThrowError(); // platform-states diff --git a/packages/catalog-platformstate-writer/test/utils.test.ts b/packages/catalog-platformstate-writer/test/utils.test.ts index 91c5191f25..86ba1eda33 100644 --- a/packages/catalog-platformstate-writer/test/utils.test.ts +++ b/packages/catalog-platformstate-writer/test/utils.test.ts @@ -31,6 +31,7 @@ import { writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { z } from "zod"; +import { genericLogger } from "pagopa-interop-commons"; import { deleteCatalogEntry, descriptorStateToItemState, @@ -68,7 +69,8 @@ describe("utils tests", async () => { dynamoDBClient, primaryKey, itemState.active, - 1 + 1, + genericLogger ) ).rejects.toThrowError(ConditionalCheckFailedException); const catalogEntry = await readCatalogEntry(primaryKey, dynamoDBClient); @@ -91,12 +93,17 @@ describe("utils tests", async () => { expect( await readCatalogEntry(primaryKey, dynamoDBClient) ).toBeUndefined(); - await writeCatalogEntry(previousCatalogStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousCatalogStateEntry, + dynamoDBClient, + genericLogger + ); await updateDescriptorStateInPlatformStatesEntry( dynamoDBClient, primaryKey, itemState.active, - 2 + 2, + genericLogger ); const result = await readCatalogEntry(primaryKey, dynamoDBClient); @@ -125,9 +132,9 @@ describe("utils tests", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(catalogEntry, dynamoDBClient); + await writeCatalogEntry(catalogEntry, dynamoDBClient, genericLogger); expect( - writeCatalogEntry(catalogEntry, dynamoDBClient) + writeCatalogEntry(catalogEntry, dynamoDBClient, genericLogger) ).rejects.toThrowError(ConditionalCheckFailedException); }); @@ -147,7 +154,7 @@ describe("utils tests", async () => { expect( await readCatalogEntry(primaryKey, dynamoDBClient) ).toBeUndefined(); - await writeCatalogEntry(catalogStateEntry, dynamoDBClient); + await writeCatalogEntry(catalogStateEntry, dynamoDBClient, genericLogger); const retrievedCatalogEntry = await readCatalogEntry( primaryKey, dynamoDBClient @@ -180,7 +187,11 @@ describe("utils tests", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousCatalogStateEntry, dynamoDBClient); + await writeCatalogEntry( + previousCatalogStateEntry, + dynamoDBClient, + genericLogger + ); const retrievedCatalogEntry = await readCatalogEntry( primaryKey, dynamoDBClient @@ -197,7 +208,7 @@ describe("utils tests", async () => { descriptorId: generateId(), }); expect( - deleteCatalogEntry(primaryKey, dynamoDBClient) + deleteCatalogEntry(primaryKey, dynamoDBClient, genericLogger) ).resolves.not.toThrowError(); }); @@ -214,8 +225,12 @@ describe("utils tests", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(previousCatalogStateEntry, dynamoDBClient); - await deleteCatalogEntry(primaryKey, dynamoDBClient); + await writeCatalogEntry( + previousCatalogStateEntry, + dynamoDBClient, + genericLogger + ); + await deleteCatalogEntry(primaryKey, dynamoDBClient, genericLogger); const retrievedCatalogEntry = await readCatalogEntry( primaryKey, dynamoDBClient @@ -450,7 +465,8 @@ describe("utils tests", async () => { updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, itemState.inactive, - dynamoDBClient + dynamoDBClient, + genericLogger ) ).resolves.not.toThrowError(); const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( @@ -502,7 +518,8 @@ describe("utils tests", async () => { await updateDescriptorStateInTokenGenerationStatesTable( eserviceId_descriptorId, itemState.active, - dynamoDBClient + dynamoDBClient, + genericLogger ); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV1.ts b/packages/purpose-platformstate-writer/src/consumerServiceV1.ts index 3cdd7ed0b8..2463776715 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV1.ts @@ -43,6 +43,9 @@ export async function handleMessageV1( if (existingPurposeEntry) { if (existingPurposeEntry.version > msg.version) { // Stops processing if the message is older than the purpose entry + logger.info( + `Skipping processing of entry ${existingPurposeEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { // platform-states @@ -52,6 +55,7 @@ export async function handleMessageV1( purposeState, version: msg.version, purposeVersionId: purposeVersion.id, + logger, }); // token-generation-states @@ -60,6 +64,7 @@ export async function handleMessageV1( purposeId: purpose.id, purposeState, purposeVersionId: purposeVersion.id, + logger, }); } } else { @@ -73,7 +78,7 @@ export async function handleMessageV1( version: msg.version, updatedAt: new Date().toISOString(), }; - await writePlatformPurposeEntry(dynamoDBClient, purposeEntry); + await writePlatformPurposeEntry(dynamoDBClient, purposeEntry, logger); // token-generation-states await updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData( @@ -96,6 +101,13 @@ export async function handleMessageV1( if (!existingPurposeEntry || existingPurposeEntry.version > msg.version) { // Stops processing if the message is older than the purpose entry or if it doesn't exist + logger.info( + `Skipping processing of entry ${primaryKey}. Reason: ${ + !existingPurposeEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); } else { // platform-states @@ -105,6 +117,7 @@ export async function handleMessageV1( purposeState, purposeVersionId: existingPurposeEntry.purposeVersionId, version: msg.version, + logger, }); // token-generation-states @@ -113,6 +126,7 @@ export async function handleMessageV1( purposeId: purpose.id, purposeState, purposeVersionId: existingPurposeEntry.purposeVersionId, + logger, }); } }) @@ -121,7 +135,7 @@ export async function handleMessageV1( const primaryKey = makePlatformStatesPurposePK(purpose.id); // platform-states - await deletePlatformPurposeEntry(dynamoDBClient, primaryKey); + await deletePlatformPurposeEntry(dynamoDBClient, primaryKey, logger); // token-generation-states await updatePurposeDataInTokenGenStatesEntries({ @@ -129,6 +143,7 @@ export async function handleMessageV1( purposeId: purpose.id, purposeState: getPurposeStateFromPurposeVersions(purpose.versions), purposeVersionId: getLastArchivedPurposeVersion(purpose.versions).id, + logger, }); }) .with( diff --git a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts index c9def4df5b..51b7d169ad 100644 --- a/packages/purpose-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/purpose-platformstate-writer/src/consumerServiceV2.ts @@ -49,6 +49,9 @@ export async function handleMessageV2( if (existingPurposeEntry) { if (existingPurposeEntry.version > msg.version) { // Stops processing if the message is older than the purpose entry + logger.info( + `Skipping processing of entry ${existingPurposeEntry.PK}. Reason: a more recent entry already exists` + ); return Promise.resolve(); } else { // platform-states @@ -58,6 +61,7 @@ export async function handleMessageV2( purposeState, purposeVersionId: purposeVersion.id, version: msg.version, + logger, }); } } else { @@ -71,7 +75,7 @@ export async function handleMessageV2( version: msg.version, updatedAt: new Date().toISOString(), }; - await writePlatformPurposeEntry(dynamoDBClient, purposeEntry); + await writePlatformPurposeEntry(dynamoDBClient, purposeEntry, logger); } // token-generation-states @@ -107,6 +111,13 @@ export async function handleMessageV2( existingPurposeEntry.version > msg.version ) { // Stops processing if the message is older than the purpose entry or if it doesn't exist + logger.info( + `Skipping processing of entry ${primaryKey}. Reason: ${ + !existingPurposeEntry + ? "entry not found in platform-states" + : "a more recent entry already exists" + }` + ); return Promise.resolve(); } else { // platform-states @@ -116,6 +127,7 @@ export async function handleMessageV2( purposeState, purposeVersionId, version: msg.version, + logger, }); // token-generation-states @@ -124,6 +136,7 @@ export async function handleMessageV2( purposeId: purpose.id, purposeState, purposeVersionId, + logger, }); } } @@ -136,7 +149,7 @@ export async function handleMessageV2( }); // platform-states - await deletePlatformPurposeEntry(dynamoDBClient, primaryKey); + await deletePlatformPurposeEntry(dynamoDBClient, primaryKey, logger); // token-generation-states await updatePurposeDataInTokenGenStatesEntries({ @@ -144,6 +157,7 @@ export async function handleMessageV2( purposeId: purpose.id, purposeState: getPurposeStateFromPurposeVersions(purpose.versions), purposeVersionId: unsafeBrandId(msg.data.versionId), + logger, }); }) .with( diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index a411d3439c..54baeacead 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -53,7 +53,8 @@ export const getPurposeStateFromPurposeVersions = ( export const writePlatformPurposeEntry = async ( dynamoDBClient: DynamoDBClient, - purposeEntry: PlatformStatesPurposeEntry + purposeEntry: PlatformStatesPurposeEntry, + logger: Logger ): Promise => { const input: PutItemInput = { ConditionExpression: "attribute_not_exists(PK)", @@ -84,6 +85,7 @@ export const writePlatformPurposeEntry = async ( }; const command = new PutItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Written purpose entry ${purposeEntry.PK}`); }; export const readPlatformPurposeEntry = async ( @@ -118,7 +120,8 @@ export const readPlatformPurposeEntry = async ( export const deletePlatformPurposeEntry = async ( dynamoDBClient: DynamoDBClient, - primaryKey: PlatformStatesPurposePK + primaryKey: PlatformStatesPurposePK, + logger: Logger ): Promise => { const input: DeleteItemInput = { Key: { @@ -128,6 +131,7 @@ export const deletePlatformPurposeEntry = async ( }; const command = new DeleteItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Deleted purpose entry ${primaryKey}`); }; export const readTokenGenStatesEntriesByGSIPKPurposeId = async ( @@ -184,12 +188,14 @@ export const updatePurposeDataInPlatformStatesEntry = async ({ purposeState, purposeVersionId, version, + logger, }: { dynamoDBClient: DynamoDBClient; primaryKey: PlatformStatesPurposePK; purposeState: ItemState; purposeVersionId: PurposeVersionId; version: number; + logger: Logger; }): Promise => { const input: UpdateItemInput = { ConditionExpression: "attribute_exists(PK)", @@ -222,6 +228,7 @@ export const updatePurposeDataInPlatformStatesEntry = async ({ }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Platform-states. Updated purpose entry ${primaryKey}`); }; export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = @@ -393,6 +400,9 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info( + `Token-generation-states. Updated entry ${tokenEntryPK} with purpose and platform-states data` + ); } if (result.lastEvaluatedKey) { @@ -419,11 +429,13 @@ export const updatePurposeDataInTokenGenStatesEntries = async ({ purposeId, purposeState, purposeVersionId, + logger, }: { dynamoDBClient: DynamoDBClient; purposeId: PurposeId; purposeState: ItemState; purposeVersionId: PurposeVersionId; + logger: Logger; }): Promise => { const runPaginatedUpdateQuery = async ( dynamoDBClient: DynamoDBClient, @@ -464,6 +476,7 @@ export const updatePurposeDataInTokenGenStatesEntries = async ({ }; const command = new UpdateItemCommand(input); await dynamoDBClient.send(command); + logger.info(`Token-generation-states. Updated entry ${entry.PK}`); } if (result.lastEvaluatedKey) { diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts index c6f021c7c3..68d52b7a64 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -282,7 +282,8 @@ describe("integration tests for events V1", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -367,7 +368,8 @@ describe("integration tests for events V1", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -501,7 +503,11 @@ describe("integration tests for events V1", () => { version: previousEntryVersion, updatedAt: mockDate.toISOString(), }; - await writePlatformPurposeEntry(dynamoDBClient, previousStateEntry); + await writePlatformPurposeEntry( + dynamoDBClient, + previousStateEntry, + genericLogger + ); // token-generation-states const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = @@ -782,7 +788,8 @@ describe("integration tests for events V1", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -885,7 +892,8 @@ describe("integration tests for events V1", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1115,7 +1123,8 @@ describe("integration tests for events V1", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts index 1d978a07d6..77990c7122 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -284,7 +284,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -371,7 +372,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -640,7 +642,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -746,7 +749,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1066,7 +1070,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1154,7 +1159,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1424,7 +1430,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1529,7 +1536,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1653,7 +1661,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1783,7 +1792,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -1888,7 +1898,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2012,7 +2023,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2143,7 +2155,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2250,7 +2263,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2376,7 +2390,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2507,7 +2522,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2614,7 +2630,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2740,7 +2757,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states @@ -2870,7 +2888,8 @@ describe("integration tests for events V2", () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); // token-generation-states diff --git a/packages/purpose-platformstate-writer/test/utils.test.ts b/packages/purpose-platformstate-writer/test/utils.test.ts index 1356272a0a..c98c8dc98f 100644 --- a/packages/purpose-platformstate-writer/test/utils.test.ts +++ b/packages/purpose-platformstate-writer/test/utils.test.ts @@ -123,7 +123,8 @@ describe("utils tests", async () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, @@ -148,9 +149,17 @@ describe("utils tests", async () => { version: 1, updatedAt: new Date().toISOString(), }; - await writePlatformPurposeEntry(dynamoDBClient, platformPurposeEntry); + await writePlatformPurposeEntry( + dynamoDBClient, + platformPurposeEntry, + genericLogger + ); await expect( - writePlatformPurposeEntry(dynamoDBClient, platformPurposeEntry) + writePlatformPurposeEntry( + dynamoDBClient, + platformPurposeEntry, + genericLogger + ) ).rejects.toThrowError(ConditionalCheckFailedException); }); @@ -168,7 +177,11 @@ describe("utils tests", async () => { expect( await readPlatformPurposeEntry(dynamoDBClient, primaryKey) ).toBeUndefined(); - await writePlatformPurposeEntry(dynamoDBClient, platformPurposeEntry); + await writePlatformPurposeEntry( + dynamoDBClient, + platformPurposeEntry, + genericLogger + ); const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, primaryKey @@ -182,7 +195,7 @@ describe("utils tests", async () => { it("should do no operation if previous entry doesn't exist", async () => { const primaryKey = makePlatformStatesPurposePK(generateId()); await expect( - deletePlatformPurposeEntry(dynamoDBClient, primaryKey) + deletePlatformPurposeEntry(dynamoDBClient, primaryKey, genericLogger) ).resolves.not.toThrowError(); }); @@ -199,9 +212,14 @@ describe("utils tests", async () => { }; await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger + ); + await deletePlatformPurposeEntry( + dynamoDBClient, + primaryKey, + genericLogger ); - await deletePlatformPurposeEntry(dynamoDBClient, primaryKey); const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( dynamoDBClient, primaryKey @@ -390,6 +408,7 @@ describe("utils tests", async () => { purposeState: itemState.active, version: 2, purposeVersionId: generateId(), + logger: genericLogger, }) ).rejects.toThrowError(ConditionalCheckFailedException); const platformPurposeEntry = await readPlatformPurposeEntry( @@ -418,7 +437,8 @@ describe("utils tests", async () => { ).toBeUndefined(); await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); await updatePurposeDataInPlatformStatesEntry({ dynamoDBClient, @@ -426,6 +446,7 @@ describe("utils tests", async () => { purposeState: itemState.active, purposeVersionId, version: 2, + logger: genericLogger, }); const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -462,7 +483,8 @@ describe("utils tests", async () => { ).toBeUndefined(); await writePlatformPurposeEntry( dynamoDBClient, - previousPlatformPurposeEntry + previousPlatformPurposeEntry, + genericLogger ); const newPurposeVersionId = generateId(); await updatePurposeDataInPlatformStatesEntry({ @@ -471,6 +493,7 @@ describe("utils tests", async () => { purposeState: itemState.active, version: 2, purposeVersionId: newPurposeVersionId, + logger: genericLogger, }); const retrievedPlatformPurposeEntry = await readPlatformPurposeEntry( @@ -508,6 +531,7 @@ describe("utils tests", async () => { purposeId: purpose.id, purposeState: itemState.inactive, purposeVersionId: purpose.versions[0].id, + logger: genericLogger, }) ).resolves.not.toThrowError(); const tokenGenStatesEntriesAfterUpdate = await readAllTokenGenStatesItems( @@ -563,6 +587,7 @@ describe("utils tests", async () => { purposeId: purpose.id, purposeState: itemState.active, purposeVersionId: newPurposeVersionId, + logger: genericLogger, }); const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient From 1580917a416851de9909ee16f574e57834f4f0d3 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Fri, 10 Jan 2025 17:58:56 +0100 Subject: [PATCH 101/126] Set ConsistentRead in platformstate-writers (#1354) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 26 ++- .../src/consumerServiceV2.ts | 20 ++- .../src/utils.ts | 22 ++- .../test/utils.test.ts | 4 + .../src/consumerServiceV1.ts | 4 +- .../src/consumerServiceV2.ts | 7 +- .../src/utils.ts | 84 +++++++-- .../consumerServiceV2.integration.test.ts | 29 +++- .../test/utils.test.ts | 160 +++++++++++++++--- .../catalog-platformstate-writer/src/utils.ts | 1 + .../purpose-platformstate-writer/src/utils.ts | 2 + 11 files changed, 297 insertions(+), 62 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index eeba6cc8b7..63402072c3 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -25,6 +25,7 @@ import { updateAgreementStateAndDescriptorInfoOnTokenGenStates, deleteAgreementEntry, isLatestAgreement, + extractAgreementTimestamp, } from "./utils.js"; export async function handleMessageV1( @@ -141,6 +142,8 @@ const handleActivationOrSuspension = async ( eserviceId: agreement.eserviceId, }); + const agreementTimestamp = extractAgreementTimestamp(agreement); + if (existingAgreementEntry) { if (existingAgreementEntry.version > incomingVersion) { // Stops processing if the message is older than the agreement entry @@ -158,9 +161,6 @@ const handleActivationOrSuspension = async ( ); } } else { - const agreementTimestamp = agreement.stamps.activation - ? agreement.stamps.activation.when.toISOString() - : agreement.createdAt.toISOString(); if (agreement.stamps.activation === undefined) { logger.warn( `Missing agreement activation stamp for agreement with id ${agreement.id}. Using createdAt as fallback.` @@ -184,6 +184,7 @@ const handleActivationOrSuspension = async ( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { @@ -226,10 +227,13 @@ const handleArchiving = async ( eserviceId: agreement.eserviceId, }); + const agreementTimestamp = extractAgreementTimestamp(agreement); + if ( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { @@ -270,6 +274,8 @@ const handleUpgrade = async ( eserviceId: agreement.eserviceId, }); + const agreementTimestamp = extractAgreementTimestamp(agreement); + if (agreementEntry) { if (agreementEntry.version > msgVersion) { logger.info( @@ -286,11 +292,6 @@ const handleUpgrade = async ( ); } } else { - const agreementTimestamp = agreement.stamps.upgrade - ? agreement.stamps.upgrade.when.toISOString() - : agreement.stamps.activation - ? agreement.stamps.activation.when.toISOString() - : agreement.createdAt.toISOString(); if (agreement.stamps.upgrade === undefined) { logger.warn( `Missing agreement upgrade stamp for agreement with id ${agreement.id}. Using activation stamp or createdAt as fallback.` @@ -312,12 +313,14 @@ const handleUpgrade = async ( const updateLatestAgreementOnTokenGenStates = async ( catalogEntry: PlatformStatesCatalogEntry | undefined, + agreementTimestamp: string, logger: Logger ): Promise => { if ( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { @@ -343,7 +346,11 @@ const handleUpgrade = async ( } }; - await updateLatestAgreementOnTokenGenStates(catalogEntry, logger); + await updateLatestAgreementOnTokenGenStates( + catalogEntry, + agreementTimestamp, + logger + ); const secondRetrievalCatalogEntry = await readCatalogEntry( pkCatalogEntry, @@ -355,6 +362,7 @@ const handleUpgrade = async ( ) { await updateLatestAgreementOnTokenGenStates( secondRetrievalCatalogEntry, + agreementTimestamp, logger ); } else { diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index abbf72fa2c..505bebcdf6 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -24,6 +24,7 @@ import { updateAgreementStateAndDescriptorInfoOnTokenGenStates, writeAgreementEntry, isLatestAgreement, + extractAgreementTimestamp, } from "./utils.js"; export async function handleMessageV2( @@ -45,6 +46,8 @@ export async function handleMessageV2( eserviceId: agreement.eserviceId, }); + const agreementTimestamp = extractAgreementTimestamp(agreement); + if (existingAgreementEntry) { if (existingAgreementEntry.version > msg.version) { // Stops processing if the message is older than the agreement entry @@ -85,6 +88,7 @@ export async function handleMessageV2( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { @@ -133,6 +137,8 @@ export async function handleMessageV2( dynamoDBClient ); + const agreementTimestamp = extractAgreementTimestamp(agreement); + if (!agreementEntry || agreementEntry.version > msg.version) { logger.info( `Skipping processing of entry ${primaryKey}. Reason: ${ @@ -160,6 +166,7 @@ export async function handleMessageV2( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { @@ -233,12 +240,14 @@ export async function handleMessageV2( const updateLatestAgreementOnTokenGenStates = async ( catalogEntry: PlatformStatesCatalogEntry | undefined, + agreementTimestamp: string, logger: Logger ): Promise => { if ( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { @@ -271,7 +280,12 @@ export async function handleMessageV2( dynamoDBClient ); - await updateLatestAgreementOnTokenGenStates(catalogEntry, logger); + const agreementTimestamp = extractAgreementTimestamp(agreement); + await updateLatestAgreementOnTokenGenStates( + catalogEntry, + agreementTimestamp, + logger + ); const updatedCatalogEntry = await readCatalogEntry( pkCatalogEntry, @@ -284,6 +298,7 @@ export async function handleMessageV2( ) { await updateLatestAgreementOnTokenGenStates( updatedCatalogEntry, + agreementTimestamp, logger ); } else { @@ -306,10 +321,13 @@ export async function handleMessageV2( eserviceId: agreement.eserviceId, }); + const agreementTimestamp = extractAgreementTimestamp(agreement); + if ( await isLatestAgreement( GSIPK_consumerId_eserviceId, agreement.id, + agreementTimestamp, dynamoDBClient ) ) { diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index 9a03e5505a..e4523a4d58 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -13,6 +13,7 @@ import { PlatformStatesEServiceDescriptorPK, PlatformStatesAgreementGSIAgreement, TokenGenStatesConsumerClientGSIAgreement, + Agreement, } from "pagopa-interop-models"; import { AttributeValue, @@ -81,6 +82,7 @@ export const readAgreementEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); @@ -544,6 +546,7 @@ export const readCatalogEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); @@ -565,9 +568,16 @@ export const readCatalogEntry = async ( } }; +/* +Because of DynamoDB eventual consistency, it's possible that the result of querying platform-states by +GSIPK_consumerId_eserviceId doesn't contain the entry with the latest agreement. +In this case, we need to check if the input agreement timestamp is more recent or equal than GSISK_agreementTimestamp +of the latest agreement in platform-states. +*/ export const isLatestAgreement = async ( GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId, agreementId: AgreementId, + currentAgreementTimestamp: string, dynamoDBClient: DynamoDBClient ): Promise => { const agreementEntries = @@ -582,5 +592,15 @@ export const isLatestAgreement = async ( const agreementIdFromEntry = extractAgreementIdFromAgreementPK( agreementEntries[0].PK ); - return agreementIdFromEntry === agreementId; + + return ( + agreementIdFromEntry === agreementId || + new Date(currentAgreementTimestamp) >= + new Date(agreementEntries[0].GSISK_agreementTimestamp) + ); }; + +export const extractAgreementTimestamp = (agreement: Agreement): string => + agreement.stamps.upgrade?.when.toISOString() || + agreement.stamps.activation?.when.toISOString() || + agreement.createdAt.toISOString(); diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index 6d4b5232e3..0bc2870c6f 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -696,6 +696,7 @@ describe("utils", async () => { await isLatestAgreement( GSIPK_consumerId_eserviceId, agreementId1, + agreementEntry1.GSISK_agreementTimestamp, dynamoDBClient ) ).toEqual(true); @@ -704,6 +705,7 @@ describe("utils", async () => { await isLatestAgreement( GSIPK_consumerId_eserviceId, agreementId2, + agreementEntry2.GSISK_agreementTimestamp, dynamoDBClient ) ).toEqual(false); @@ -713,6 +715,7 @@ describe("utils", async () => { const eserviceId = generateId(); const consumerId = generateId(); const agreementId1 = generateId(); + const agreementTimestamp = new Date().toISOString(); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, @@ -723,6 +726,7 @@ describe("utils", async () => { await isLatestAgreement( GSIPK_consumerId_eserviceId, agreementId1, + agreementTimestamp, dynamoDBClient ) ).toEqual(true); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index d19fcd1909..c7abb0a9df 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -32,8 +32,8 @@ import { createTokenGenStatesConsumerClient, deleteClientEntryFromPlatformStates, deleteClientEntryFromTokenGenerationStates, - deleteEntriesFromTokenGenStatesByClientId, deleteEntriesFromTokenGenStatesByClientIdKid, + deleteEntriesFromTokenGenStatesByClientIdV1, deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, extractAgreementIdFromAgreementPK, extractKidFromTokenGenStatesEntryPK, @@ -512,7 +512,7 @@ export async function handleMessageV1( await deleteClientEntryFromPlatformStates(pk, dynamoDBClient, logger); const GSIPK_clientId = clientId; - await deleteEntriesFromTokenGenStatesByClientId( + await deleteEntriesFromTokenGenStatesByClientIdV1( GSIPK_clientId, dynamoDBClient, logger diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index c629be58eb..90e0fe620b 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -27,7 +27,6 @@ import { clientKindToTokenGenerationStatesClientKind, convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, - deleteEntriesFromTokenGenStatesByClientId, deleteEntriesFromTokenGenStatesByClientIdKid, deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, readPlatformClientEntry, @@ -42,6 +41,7 @@ import { updateTokenGenStatesDataForSecondRetrieval, createTokenGenStatesConsumerClient, readConsumerClientEntriesInTokenGenerationStates, + deleteEntriesFromTokenGenStatesByClientIdV2, } from "./utils.js"; export async function handleMessageV2( @@ -473,9 +473,8 @@ export async function handleMessageV2( const pk = makePlatformStatesClientPK(client.id); await deleteClientEntryFromPlatformStates(pk, dynamoDBClient, logger); - const GSIPK_clientId = client.id; - await deleteEntriesFromTokenGenStatesByClientId( - GSIPK_clientId, + await deleteEntriesFromTokenGenStatesByClientIdV2( + client, dynamoDBClient, logger ); diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 803396bb99..c28b3878ea 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -11,6 +11,9 @@ import { QueryCommand, QueryCommandOutput, QueryInput, + ScanCommand, + ScanCommandOutput, + ScanInput, UpdateItemCommand, UpdateItemInput, } from "@aws-sdk/client-dynamodb"; @@ -48,8 +51,11 @@ import { TokenGenerationStatesConsumerClient, TokenGenStatesConsumerClientGSIClient, TokenGenStatesConsumerClientGSIClientPurpose, - TokenGenStatesGenericClientGSIClient, TokenGenStatesGenericClientGSIClientKid, + Client, + clientKidPrefix, + clientKidPurposePrefix, + TokenGenerationStatesGenericClient, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; @@ -135,27 +141,30 @@ export const deleteClientEntryFromPlatformStates = async ( logger.info(`Platform-states. Deleted client entry ${pk}`); }; -export const deleteEntriesFromTokenGenStatesByClientId = async ( +export const deleteEntriesFromTokenGenStatesByClientIdV1 = async ( GSIPK_clientId: ClientId, dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { + // We need to find all the entries to delete though a Scan, because the query on the GSI doesn't allow ConsistentRead const runPaginatedQuery = async ( - GSIPK_clientId: ClientId, + prefix: string, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { - const input: QueryInput = { + const readInput: ScanInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - IndexName: "Client", - KeyConditionExpression: `GSIPK_clientId = :gsiValue`, + FilterExpression: "begins_with(#pk, :prefix)", + ExpressionAttributeNames: { + "#pk": "PK", + }, ExpressionAttributeValues: { - ":gsiValue": { S: GSIPK_clientId }, + ":prefix": { S: prefix }, }, ExclusiveStartKey: exclusiveStartKey, }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); if (!data.Items) { throw genericInternalError( @@ -167,7 +176,7 @@ export const deleteEntriesFromTokenGenStatesByClientId = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenStatesGenericClientGSIClient) + .array(TokenGenerationStatesGenericClient) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { @@ -187,16 +196,56 @@ export const deleteEntriesFromTokenGenStatesByClientId = async ( } if (data.LastEvaluatedKey) { - await runPaginatedQuery( - GSIPK_clientId, - dynamoDBClient, - data.LastEvaluatedKey - ); + await runPaginatedQuery(prefix, dynamoDBClient, data.LastEvaluatedKey); } } }; - await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); + const prefix1 = `${clientKidPrefix}${GSIPK_clientId}`; + const prefix2 = `${clientKidPurposePrefix}${GSIPK_clientId}`; + + await runPaginatedQuery(prefix1, dynamoDBClient, undefined); + await runPaginatedQuery(prefix2, dynamoDBClient, undefined); +}; + +export const deleteEntriesFromTokenGenStatesByClientIdV2 = async ( + // For v2 events we have the entire client, so we can build all the PKs we need + client: Client, + dynamoDBClient: DynamoDBClient, + logger: Logger +): Promise => { + if (client.purposes.length > 0) { + await Promise.all( + client.keys.map((key) => + client.purposes.map(async (purpose) => { + const pk = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key.kid, + purposeId: purpose, + }); + await deleteClientEntryFromTokenGenerationStates( + pk, + dynamoDBClient, + logger + ); + }) + ) + ); + } else { + await Promise.all( + client.keys.map(async (key) => { + const pk = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + await deleteClientEntryFromTokenGenerationStates( + pk, + dynamoDBClient, + logger + ); + }) + ); + } }; export const deleteClientEntryFromTokenGenerationStates = async ( @@ -226,6 +275,7 @@ export const readPlatformClientEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); @@ -435,6 +485,7 @@ export const readPlatformCatalogEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); @@ -501,6 +552,7 @@ export const readPlatformPurposeEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index fa4ea1c986..e29abe17d4 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -2924,9 +2924,15 @@ describe("integration tests V2 events", async () => { describe("ClientDeleted", () => { it("should delete platform-states entry and token-generation-states entries", async () => { const messageVersion = 2; + const key1 = getMockKey(); + const key2 = getMockKey(); const purposeId = generateId(); - const client = getMockClient(); + const client: Client = { + ...getMockClient(), + keys: [key1, key2], + purposes: [purposeId], + }; const payload: ClientDeletedV2 = { client: toClientV2(client), @@ -2980,30 +2986,39 @@ describe("integration tests V2 events", async () => { // token-generation-states const pkTokenGenStates1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: "kid", + kid: key1.kid, purposeId, }); const pkTokenGenStates2 = makeTokenGenerationStatesClientKidPurposePK({ - clientId: otherClientId, - kid: "kid", + clientId: client.id, + kid: key2.kid, purposeId, }); - const clientPurposeTokenGenStatesEntry: TokenGenerationStatesConsumerClient = + const clientPurposeTokenGenStatesEntry1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(pkTokenGenStates1), GSIPK_clientId: client.id, }; + const clientPurposeTokenGenStatesEntry2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(pkTokenGenStates2), + GSIPK_clientId: client.id, + }; const otherClientPurposeTokenGenStatesEntry: TokenGenerationStatesConsumerClient = { - ...getMockTokenGenStatesConsumerClient(pkTokenGenStates2), + ...getMockTokenGenStatesConsumerClient(), GSIPK_clientId: otherClientId, }; await writeTokenGenStatesConsumerClient( - clientPurposeTokenGenStatesEntry, + clientPurposeTokenGenStatesEntry1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + clientPurposeTokenGenStatesEntry2, dynamoDBClient ); await writeTokenGenStatesConsumerClient( diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index ac7c1a56be..26403a463b 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -80,12 +80,13 @@ import { writeTokenGenStatesApiClient, deleteEntriesFromTokenGenStatesByClientIdKid, writePlatformClientEntry, - deleteEntriesFromTokenGenStatesByClientId, deleteClientEntryFromTokenGenerationStates, readPlatformClientEntry, deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, upsertTokenGenStatesConsumerClient, upsertTokenGenStatesApiClient, + deleteEntriesFromTokenGenStatesByClientIdV2, + deleteEntriesFromTokenGenStatesByClientIdV1, } from "../src/utils.js"; import { dynamoDBClient } from "./utils.js"; @@ -173,44 +174,159 @@ describe("utils", () => { expect(res).toEqual([clientEntry2]); }); - it("deleteEntriesFromTokenGenStatesByClientId", async () => { - const GSIPK_clientId = generateId(); + it("deleteEntriesFromTokenGenStatesByClientIdV1 - ApiClient", async () => { + const mockKey1 = getMockKey(); + const mockKey2 = getMockKey(); - const clientEntry: TokenGenerationStatesApiClient = { - ...getMockTokenGenStatesApiClient(), - GSIPK_clientId, + const client: Client = { + ...getMockClient(), + keys: [mockKey1, mockKey2], + purposes: [], }; - const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { - ...getMockTokenGenStatesConsumerClient(), - GSIPK_clientId, + const apiClient: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: mockKey1.kid, + }), + GSIPK_clientId: client.id, }; - const otherConsumerClient: TokenGenerationStatesConsumerClient = { - ...getMockTokenGenStatesConsumerClient(), + const otherApiClient: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), }; await writeTokenGenStatesApiClient( - clientEntry, + apiClient, dynamoDBClient, genericLogger ); - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient, - dynamoDBClient - ); - await writeTokenGenStatesConsumerClient( - otherConsumerClient, - dynamoDBClient + await writeTokenGenStatesApiClient( + otherApiClient, + dynamoDBClient, + genericLogger ); - await deleteEntriesFromTokenGenStatesByClientId( - GSIPK_clientId, + await deleteEntriesFromTokenGenStatesByClientIdV1( + client.id, dynamoDBClient, genericLogger ); const result = await readAllTokenGenStatesItems(dynamoDBClient); - expect(result).toEqual([otherConsumerClient]); + expect(result).toEqual([otherApiClient]); + }); + + describe("deleteEntriesFromTokenGenStatesByClientIdV2", () => { + it("ApiClient", async () => { + const mockKey = getMockKey(); + const mockKey2 = getMockKey(); + + const client: Client = { + ...getMockClient(), + keys: [mockKey, mockKey2], + purposes: [], + }; + + const clientEntry: TokenGenerationStatesApiClient = { + GSIPK_clientId: client.id, + consumerId: client.consumerId, + updatedAt: new Date().toISOString(), + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: mockKey.kid, + }), + clientKind: clientKindTokenGenStates.api, + publicKey: "public key", + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: mockKey.kid, + }), + }; + + const otherApiClient: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + }; + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); + + await writeTokenGenStatesApiClient( + otherApiClient, + dynamoDBClient, + genericLogger + ); + + await deleteEntriesFromTokenGenStatesByClientIdV2( + client, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherApiClient]); + }); + + it("ConsumerClient", async () => { + const mockKey = getMockKey(); + const mockKey2 = getMockKey(); + const purposeId = generateId(); + + const client: Client = { + ...getMockClient(), + keys: [mockKey, mockKey2], + purposes: [purposeId], + }; + + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(), + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockKey.kid, + purposeId, + }), + consumerId: client.consumerId, + }; + + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(), + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockKey2.kid, + purposeId, + }), + consumerId: client.consumerId, + }; + + const otherConsumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + otherConsumerClient, + dynamoDBClient + ); + + await deleteEntriesFromTokenGenStatesByClientIdV2( + client, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherConsumerClient]); + }); }); describe("deleteClientEntryFromTokenGenerationStates", () => { diff --git a/packages/catalog-platformstate-writer/src/utils.ts b/packages/catalog-platformstate-writer/src/utils.ts index bc0f992d47..f32a06b20e 100644 --- a/packages/catalog-platformstate-writer/src/utils.ts +++ b/packages/catalog-platformstate-writer/src/utils.ts @@ -75,6 +75,7 @@ export const readCatalogEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index 54baeacead..bd8cc7c04b 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -97,6 +97,7 @@ export const readPlatformPurposeEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); @@ -543,6 +544,7 @@ export const readCatalogEntry = async ( PK: { S: primaryKey }, }, TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; const command = new GetItemCommand(input); const data: GetItemCommandOutput = await dynamoDBClient.send(command); From bdbda0406274e653f9f661e8349a50cea652deda Mon Sep 17 00:00:00 2001 From: Andrea Zerbini Date: Mon, 13 Jan 2025 09:54:20 +0100 Subject: [PATCH 102/126] Fix generated schemas/types (#1350) --- packages/api-clients/generate.ts | 1 + packages/api-clients/open-api/bffApi.yml | 13 +------------ .../src/services/producerKeychainService.ts | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/packages/api-clients/generate.ts b/packages/api-clients/generate.ts index 38c63f0a4c..67b6c12c4a 100644 --- a/packages/api-clients/generate.ts +++ b/packages/api-clients/generate.ts @@ -79,6 +79,7 @@ const main = async () => { groupStrategy: "tag", strictObjects: true, apiClientName: fileName, + additionalPropertiesDefaultValue: false, }, }); } diff --git a/packages/api-clients/open-api/bffApi.yml b/packages/api-clients/open-api/bffApi.yml index 32bf011560..f43d12fe19 100644 --- a/packages/api-clients/open-api/bffApi.yml +++ b/packages/api-clients/open-api/bffApi.yml @@ -14732,7 +14732,7 @@ components: eservices: type: array items: - $ref: "#/components/schemas/ProducerKeychainEService" + $ref: "#/components/schemas/CompactEServiceLight" description: type: string required: @@ -14742,17 +14742,6 @@ components: - eservices - createdAt - description - ProducerKeychainEService: - type: object - properties: - id: - type: string - format: uuid - name: - type: string - required: - - id - - name EServiceAdditionDetailsSeed: type: object properties: diff --git a/packages/backend-for-frontend/src/services/producerKeychainService.ts b/packages/backend-for-frontend/src/services/producerKeychainService.ts index e1d487afaf..36eeedb1f0 100644 --- a/packages/backend-for-frontend/src/services/producerKeychainService.ts +++ b/packages/backend-for-frontend/src/services/producerKeychainService.ts @@ -352,7 +352,7 @@ async function enhanceEService( { catalogProcessClient, tenantProcessClient }: PagoPAInteropBeClients, eserviceId: string, { headers }: WithLogger -): Promise { +): Promise { const eservice = await catalogProcessClient.getEServiceById({ params: { eServiceId: eserviceId }, headers, From 8fdb7e79523708e4f64527bcc0b578a92f96d9cc Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:16:02 +0100 Subject: [PATCH 103/126] Fix ClientPurposeAdded in authorization-platformstate-writer (#1358) --- .../src/consumerServiceV1.ts | 19 ++-- .../src/consumerServiceV2.ts | 101 ++++++------------ .../src/utils.ts | 81 +++++++------- .../consumerServiceV2.integration.test.ts | 65 ++++++----- .../test/utils.test.ts | 15 +-- .../token-generation-states-entry.ts | 33 ------ 6 files changed, 125 insertions(+), 189 deletions(-) diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index c7abb0a9df..fed6b52aae 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -37,7 +37,7 @@ import { deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, extractAgreementIdFromAgreementPK, extractKidFromTokenGenStatesEntryPK, - readConsumerClientEntriesInTokenGenerationStates, + readConsumerClientsInTokenGenStatesV1, readPlatformClientEntry, retrievePlatformStatesByPurpose, setClientPurposeIdsInPlatformStatesEntry, @@ -342,15 +342,11 @@ export async function handleMessageV1( logger ); - const GSIPK_clientId = clientId; const tokenGenStatesConsumerClients = - await readConsumerClientEntriesInTokenGenerationStates( - GSIPK_clientId, - dynamoDBClient - ); + await readConsumerClientsInTokenGenStatesV1(clientId, dynamoDBClient); if (tokenGenStatesConsumerClients.length === 0) { logger.info( - `Skipping token-generation-states update. Reason: no entries found for GSIPK_clientId ${GSIPK_clientId}` + `Skipping token-generation-states update. Reason: no entries found for client ${clientId}` ); return Promise.resolve(); } else { @@ -372,8 +368,9 @@ export async function handleMessageV1( .with(0, async () => { const newTokenGenStatesConsumerClient = createTokenGenStatesConsumerClient({ - tokenGenStatesClient: entry, + consumerId: entry.consumerId, kid: extractKidFromTokenGenStatesEntryPK(entry.PK), + publicKey: entry.publicKey, clientId, purposeId, purposeEntry, @@ -398,8 +395,9 @@ export async function handleMessageV1( if (!seenKids.has(kid)) { const newTokenGenStatesConsumerClient = createTokenGenStatesConsumerClient({ - tokenGenStatesClient: entry, + consumerId: entry.consumerId, kid, + publicKey: entry.publicKey, clientId, purposeId, purposeEntry, @@ -511,9 +509,8 @@ export async function handleMessageV1( const pk = makePlatformStatesClientPK(clientId); await deleteClientEntryFromPlatformStates(pk, dynamoDBClient, logger); - const GSIPK_clientId = clientId; await deleteEntriesFromTokenGenStatesByClientIdV1( - GSIPK_clientId, + clientId, dynamoDBClient, logger ); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 90e0fe620b..eb0fb24736 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -21,7 +21,7 @@ import { TokenGenerationStatesConsumerClient, unsafeBrandId, } from "pagopa-interop-models"; -import { match, P } from "ts-pattern"; +import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; import { clientKindToTokenGenerationStatesClientKind, @@ -31,7 +31,6 @@ import { deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, readPlatformClientEntry, deleteClientEntryFromTokenGenerationStates, - extractKidFromTokenGenStatesEntryPK, extractAgreementIdFromAgreementPK, retrievePlatformStatesByPurpose, upsertPlatformClientEntry, @@ -40,7 +39,6 @@ import { setClientPurposeIdsInPlatformStatesEntry, updateTokenGenStatesDataForSecondRetrieval, createTokenGenStatesConsumerClient, - readConsumerClientEntriesInTokenGenerationStates, deleteEntriesFromTokenGenStatesByClientIdV2, } from "./utils.js"; @@ -312,15 +310,9 @@ export async function handleMessageV2( } // token-generation-states - const GSIPK_clientId = client.id; - const tokenGenStatesConsumerClients = - await readConsumerClientEntriesInTokenGenerationStates( - GSIPK_clientId, - dynamoDBClient - ); - if (tokenGenStatesConsumerClients.length === 0) { + if (client.keys.length === 0) { logger.info( - `Skipping token-generation-states update. Reason: no entries found for GSIPK_clientId ${GSIPK_clientId}` + `Skipping token-generation-states update. Reason: client ${client.id} has zero keys` ); return Promise.resolve(); } else { @@ -332,71 +324,44 @@ export async function handleMessageV2( logger ); - const seenKids = new Set(); const addedTokenGenStatesConsumerClients: TokenGenerationStatesConsumerClient[] = []; - for (const entry of tokenGenStatesConsumerClients) { - const addedTokenGenStatesConsumerClient = await match( - client.purposes.length - ) - .with(1, async () => { - const newTokenGenStatesConsumerClient = - createTokenGenStatesConsumerClient({ - tokenGenStatesClient: entry, - kid: extractKidFromTokenGenStatesEntryPK(entry.PK), - clientId: client.id, - purposeId, - purposeEntry, - agreementEntry, - catalogEntry, - }); - - await upsertTokenGenStatesConsumerClient( - newTokenGenStatesConsumerClient, - dynamoDBClient, - logger - ); - await deleteClientEntryFromTokenGenerationStates( - entry.PK, - dynamoDBClient, - logger - ); - return newTokenGenStatesConsumerClient; - }) - .with(P.number.gt(1), async () => { - const kid = extractKidFromTokenGenStatesEntryPK(entry.PK); - if (!seenKids.has(kid)) { - const newTokenGenStatesConsumerClient = - createTokenGenStatesConsumerClient({ - tokenGenStatesClient: entry, - kid, - clientId: client.id, - purposeId, - purposeEntry, - agreementEntry, - catalogEntry, - }); + await Promise.all( + client.keys.map(async (key) => { + const newTokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + createTokenGenStatesConsumerClient({ + consumerId: client.consumerId, + kid: key.kid, + publicKey: key.encodedPem, + clientId: client.id, + purposeId, + purposeEntry, + agreementEntry, + catalogEntry, + }); - await upsertTokenGenStatesConsumerClient( - newTokenGenStatesConsumerClient, - dynamoDBClient, - logger - ); - seenKids.add(kid); - return newTokenGenStatesConsumerClient; - } - return null; - }) - .run(); + await upsertTokenGenStatesConsumerClient( + newTokenGenStatesConsumerClient, + dynamoDBClient, + logger + ); - if (addedTokenGenStatesConsumerClient) { // eslint-disable-next-line functional/immutable-data addedTokenGenStatesConsumerClients.push( - addedTokenGenStatesConsumerClient + newTokenGenStatesConsumerClient ); - } - } + + await deleteClientEntryFromTokenGenerationStates( + makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }), + dynamoDBClient, + logger + ); + }) + ); // Second check for updated fields await Promise.all( diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index c28b3878ea..09ca105436 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -12,7 +12,6 @@ import { QueryCommandOutput, QueryInput, ScanCommand, - ScanCommandOutput, ScanInput, UpdateItemCommand, UpdateItemInput, @@ -49,13 +48,12 @@ import { TokenGenerationStatesClientKidPK, TokenGenerationStatesClientKidPurposePK, TokenGenerationStatesConsumerClient, - TokenGenStatesConsumerClientGSIClient, TokenGenStatesConsumerClientGSIClientPurpose, TokenGenStatesGenericClientGSIClientKid, Client, - clientKidPrefix, - clientKidPurposePrefix, TokenGenerationStatesGenericClient, + TenantId, + makeGSIPKClientIdKid, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; @@ -142,29 +140,30 @@ export const deleteClientEntryFromPlatformStates = async ( }; export const deleteEntriesFromTokenGenStatesByClientIdV1 = async ( - GSIPK_clientId: ClientId, + clientId: ClientId, dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { - // We need to find all the entries to delete though a Scan, because the query on the GSI doesn't allow ConsistentRead + // We need to find all the entries to delete through a Scan, because the query on the GSI doesn't allow ConsistentRead const runPaginatedQuery = async ( - prefix: string, + clientId: ClientId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { const readInput: ScanInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - FilterExpression: "begins_with(#pk, :prefix)", + FilterExpression: "contains(#pk, :clientId)", ExpressionAttributeNames: { "#pk": "PK", }, ExpressionAttributeValues: { - ":prefix": { S: prefix }, + ":clientId": { S: clientId }, }, + ConsistentRead: true, ExclusiveStartKey: exclusiveStartKey, }; const commandQuery = new ScanCommand(readInput); - const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); + const data = await dynamoDBClient.send(commandQuery); if (!data.Items) { throw genericInternalError( @@ -196,16 +195,16 @@ export const deleteEntriesFromTokenGenStatesByClientIdV1 = async ( } if (data.LastEvaluatedKey) { - await runPaginatedQuery(prefix, dynamoDBClient, data.LastEvaluatedKey); + await runPaginatedQuery( + clientId, + dynamoDBClient, + data.LastEvaluatedKey + ); } } }; - const prefix1 = `${clientKidPrefix}${GSIPK_clientId}`; - const prefix2 = `${clientKidPurposePrefix}${GSIPK_clientId}`; - - await runPaginatedQuery(prefix1, dynamoDBClient, undefined); - await runPaginatedQuery(prefix2, dynamoDBClient, undefined); + await runPaginatedQuery(clientId, dynamoDBClient, undefined); }; export const deleteEntriesFromTokenGenStatesByClientIdV2 = async ( @@ -216,7 +215,7 @@ export const deleteEntriesFromTokenGenStatesByClientIdV2 = async ( ): Promise => { if (client.purposes.length > 0) { await Promise.all( - client.keys.map((key) => + client.keys.flatMap((key) => client.purposes.map(async (purpose) => { const pk = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, @@ -860,27 +859,29 @@ export const writePlatformClientEntry = async ( logger.info(`Platform-states. Written client entry ${clientEntry.PK}`); }; -export const readConsumerClientEntriesInTokenGenerationStates = async ( - GSIPK_clientId: ClientId, +export const readConsumerClientsInTokenGenStatesV1 = async ( + clientId: ClientId, dynamoDBClient: DynamoDBClient -): Promise => { +): Promise => { const runPaginatedQuery = async ( - GSIPK_clientId: ClientId, + clientId: ClientId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record - ): Promise => { - const input: QueryInput = { + ): Promise => { + const input: ScanInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - IndexName: "Client", - KeyConditionExpression: `GSIPK_clientId = :gsiValue`, + FilterExpression: "contains(#pk, :clientId)", + ExpressionAttributeNames: { + "#pk": "PK", + }, ExpressionAttributeValues: { - ":gsiValue": { S: GSIPK_clientId }, + ":clientId": { S: clientId }, }, + ConsistentRead: true, ExclusiveStartKey: exclusiveStartKey, - ScanIndexForward: false, }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); + const command = new ScanCommand(input); + const data = await dynamoDBClient.send(command); if (!data.Items) { throw genericInternalError( @@ -892,7 +893,7 @@ export const readConsumerClientEntriesInTokenGenerationStates = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const clientEntries = z - .array(TokenGenStatesConsumerClientGSIClient) + .array(TokenGenerationStatesGenericClient) .safeParse(unmarshalledItems); if (!clientEntries.success) { @@ -909,7 +910,7 @@ export const readConsumerClientEntriesInTokenGenerationStates = async ( return [ ...clientEntries.data, ...(await runPaginatedQuery( - GSIPK_clientId, + clientId, dynamoDBClient, data.LastEvaluatedKey )), @@ -918,7 +919,7 @@ export const readConsumerClientEntriesInTokenGenerationStates = async ( } }; - return await runPaginatedQuery(GSIPK_clientId, dynamoDBClient, undefined); + return await runPaginatedQuery(clientId, dynamoDBClient, undefined); }; export const setClientPurposeIdsInPlatformStatesEntry = async ( @@ -1287,16 +1288,18 @@ const generateUpdateItemInputData = ( }; export const createTokenGenStatesConsumerClient = ({ - tokenGenStatesClient, + consumerId, kid, + publicKey, clientId, purposeId, purposeEntry, agreementEntry, catalogEntry, }: { - tokenGenStatesClient: TokenGenStatesConsumerClientGSIClient; + consumerId: TenantId; kid: string; + publicKey: string; clientId: ClientId; purposeId: PurposeId; purposeEntry?: PlatformStatesPurposeEntry; @@ -1311,12 +1314,12 @@ export const createTokenGenStatesConsumerClient = ({ return { PK: pk, - consumerId: tokenGenStatesClient.consumerId, + consumerId, updatedAt: new Date().toISOString(), clientKind: clientKindTokenGenStates.consumer, - publicKey: tokenGenStatesClient.publicKey, - GSIPK_clientId: tokenGenStatesClient.GSIPK_clientId, - GSIPK_clientId_kid: tokenGenStatesClient.GSIPK_clientId_kid, + publicKey, + GSIPK_clientId: clientId, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId, kid }), GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ clientId, purposeId, @@ -1324,7 +1327,7 @@ export const createTokenGenStatesConsumerClient = ({ GSIPK_purposeId: purposeId, ...(purposeEntry && { GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: tokenGenStatesClient.consumerId, + consumerId, eserviceId: purposeEntry.purposeEserviceId, }), purposeState: purposeEntry.state, diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index e29abe17d4..f118f11185 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -1853,10 +1853,13 @@ describe("integration tests V2 events", async () => { consumerId, versions: [getMockPurposeVersion(purposeVersionState.active)], }; + const key1 = getMockKey(); + const key2 = getMockKey(); const client: Client = { ...getMockClient(), consumerId, purposes: [purpose.id], + keys: [key1, key2], }; const payload: ClientPurposeAddedV2 = { @@ -1938,15 +1941,13 @@ describe("integration tests V2 events", async () => { ); // token-generation-states - const kid1 = "KID1"; - const kid2 = "KID2"; const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPK({ clientId: client.id, - kid: kid1, + kid: key1.kid, }); const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPK({ clientId: client.id, - kid: kid2, + kid: key2.kid, }); const tokenClientEntry1: TokenGenerationStatesConsumerClient = { @@ -1955,8 +1956,9 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid1, + kid: key1.kid, }), + publicKey: key1.encodedPem, }; const tokenClientEntry2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), @@ -1964,8 +1966,9 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid2, + kid: key2.kid, }), + publicKey: key2.encodedPem, }; const tokenConsumerClientWithOtherClient = getMockTokenGenStatesConsumerClient(); @@ -2033,7 +2036,7 @@ describe("integration tests V2 events", async () => { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose.id, - kid: kid1, + kid: key1.kid, }), }; @@ -2044,7 +2047,7 @@ describe("integration tests V2 events", async () => { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose.id, - kid: kid2, + kid: key2.kid, }), }; @@ -2073,10 +2076,13 @@ describe("integration tests V2 events", async () => { versions: [getMockPurposeVersion(purposeVersionState.active)], }; + const key1 = getMockKey(); + const key2 = getMockKey(); const client: Client = { ...getMockClient(), consumerId, purposes: [purpose1.id, purpose2.id], + keys: [key1, key2], }; const payload: ClientPurposeAddedV2 = { @@ -2203,18 +2209,16 @@ describe("integration tests V2 events", async () => { ); // token-generation-states - const kid1 = "KID1"; - const kid2 = "KID2"; const tokenClientKidPurposePK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: kid1, + kid: key1.kid, purposeId: purpose1.id, }); const tokenClientKidPurposePK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: kid2, + kid: key2.kid, purposeId: purpose1.id, }); @@ -2229,9 +2233,10 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid1, + kid: key1.kid, }), GSIPK_purposeId: purpose1.id, + publicKey: key1.encodedPem, }; const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), @@ -2240,9 +2245,10 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid2, + kid: key2.kid, }), GSIPK_purposeId: purpose1.id, + publicKey: key2.encodedPem, }; const tokenClientEntryWithOtherClient = getMockTokenGenStatesApiClient(); @@ -2309,7 +2315,7 @@ describe("integration tests V2 events", async () => { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, - kid: kid1, + kid: key1.kid, }), }; @@ -2320,7 +2326,7 @@ describe("integration tests V2 events", async () => { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, - kid: kid2, + kid: key2.kid, }), }; @@ -2352,10 +2358,13 @@ describe("integration tests V2 events", async () => { versions: [getMockPurposeVersion(purposeVersionState.active)], }; + const key1 = getMockKey(); + const key2 = getMockKey(); const client: Client = { ...getMockClient(), consumerId, purposes: [purpose1.id, purpose2.id], + keys: [key1, key2], }; const payload: ClientPurposeAddedV2 = { @@ -2482,26 +2491,24 @@ describe("integration tests V2 events", async () => { ); // token-generation-states - const kid1 = "KID1"; - const kid2 = "KID2"; const tokenClientKidPK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: kid1, + kid: key1.kid, purposeId: purpose1.id, }); const tokenClientKidPK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: kid2, + kid: key2.kid, purposeId: purpose1.id, }); const tokenClientKidPK3 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: kid1, + kid: key1.kid, purposeId: purpose2.id, }); const tokenClientKidPK4 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: kid2, + kid: key2.kid, purposeId: purpose2.id, }); @@ -2520,9 +2527,10 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid1, + kid: key1.kid, }), GSIPK_purposeId: purpose1.id, + publicKey: key1.encodedPem, }; const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK2), @@ -2531,9 +2539,10 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid2, + kid: key2.kid, }), GSIPK_purposeId: purpose1.id, + publicKey: key2.encodedPem, }; const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenClientKidPK3), @@ -2542,7 +2551,7 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid1, + kid: key1.kid, }), GSIPK_purposeId: purpose2.id, }; @@ -2553,7 +2562,7 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: kid2, + kid: key2.kid, }), GSIPK_purposeId: purpose2.id, }; @@ -2630,7 +2639,7 @@ describe("integration tests V2 events", async () => { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, - kid: kid1, + kid: key1.kid, }), }; const expectedTokenConsumerClient2: TokenGenerationStatesConsumerClient = @@ -2640,7 +2649,7 @@ describe("integration tests V2 events", async () => { PK: makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, purposeId: purpose2.id, - kid: kid2, + kid: key2.kid, }), }; diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 26403a463b..7d8e1e6b4e 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -54,7 +54,6 @@ import { TokenGenerationStatesApiClient, TokenGenerationStatesConsumerClient, TokenGenerationStatesGenericClient, - TokenGenStatesConsumerClientGSIClient, } from "pagopa-interop-models"; import { afterAll, @@ -72,7 +71,7 @@ import { setClientPurposeIdsInPlatformStatesEntry, convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, - readConsumerClientEntriesInTokenGenerationStates, + readConsumerClientsInTokenGenStatesV1, readPlatformAgreementEntryByGSIPKConsumerIdEServiceId, retrievePlatformStatesByPurpose, updateTokenGenStatesDataForSecondRetrieval, @@ -650,7 +649,7 @@ describe("utils", () => { }); }); - it("readConsumerClientEntriesInTokenGenerationStates", async () => { + it("readConsumerClientsInTokenGenStatesV1", async () => { const clientId = generateId(); const pk1 = makeTokenGenerationStatesClientKidPK({ clientId, kid: "" }); const pk2 = makeTokenGenerationStatesClientKidPurposePK({ @@ -682,19 +681,15 @@ describe("utils", () => { dynamoDBClient ); - const res = await readConsumerClientEntriesInTokenGenerationStates( + const res = await readConsumerClientsInTokenGenStatesV1( GSIPK_clientId, dynamoDBClient ); expect(res).toEqual( expect.arrayContaining([ - TokenGenStatesConsumerClientGSIClient.parse( - tokenGenStatesConsumerClientWithoutPurpose - ), - TokenGenStatesConsumerClientGSIClient.parse( - tokenGenStatesConsumerClientWithPurpose - ), + tokenGenStatesConsumerClientWithoutPurpose, + tokenGenStatesConsumerClientWithPurpose, ]) ); }); diff --git a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts index e8354e6b33..24116d1c7e 100644 --- a/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/token-generation-states-entry.ts @@ -86,39 +86,6 @@ export type TokenGenStatesConsumerClientGSIAgreement = z.infer< typeof TokenGenStatesConsumerClientGSIAgreement >; -// Client -export const TokenGenStatesApiClientGSIClient = - TokenGenerationStatesApiClient.pick({ - PK: true, - GSIPK_clientId: true, - consumerId: true, - clientKind: true, - publicKey: true, - GSIPK_clientId_kid: true, - }); -export type TokenGenStatesApiClientGSIClient = z.infer< - typeof TokenGenStatesApiClientGSIClient ->; - -export const TokenGenStatesConsumerClientGSIClient = - TokenGenerationStatesConsumerClient.pick({ - PK: true, - GSIPK_clientId: true, - consumerId: true, - clientKind: true, - publicKey: true, - GSIPK_clientId_kid: true, - }); -export type TokenGenStatesConsumerClientGSIClient = z.infer< - typeof TokenGenStatesConsumerClientGSIClient ->; - -export const TokenGenStatesGenericClientGSIClient = - TokenGenStatesApiClientGSIClient.or(TokenGenStatesConsumerClientGSIClient); -export type TokenGenStatesGenericClientGSIClient = z.infer< - typeof TokenGenStatesGenericClientGSIClient ->; - // ClientPurpose export const TokenGenStatesConsumerClientGSIClientPurpose = TokenGenerationStatesConsumerClient.pick({ From 1ddc8a4099f1a41097e854b8dd8acf8e3d8540be Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 Jan 2025 15:03:00 +0100 Subject: [PATCH 104/126] Fix ClientPurposeRemoved in authorization-platformstate-writer (#1356) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 4 +- .../src/consumerServiceV2.ts | 27 +-- .../src/utils.ts | 50 ++++- .../consumerServiceV2.integration.test.ts | 206 ++++++++++++++++-- .../test/utils.test.ts | 67 +++++- 5 files changed, 303 insertions(+), 51 deletions(-) diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index fed6b52aae..1a5e74691a 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -34,7 +34,7 @@ import { deleteClientEntryFromTokenGenerationStates, deleteEntriesFromTokenGenStatesByClientIdKid, deleteEntriesFromTokenGenStatesByClientIdV1, - deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, + deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1, extractAgreementIdFromAgreementPK, extractKidFromTokenGenStatesEntryPK, readConsumerClientsInTokenGenStatesV1, @@ -485,7 +485,7 @@ export async function handleMessageV1( // token-generation-states if (updatedPurposeIds.length > 0) { - await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( + await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1( GSIPK_clientId_purposeId, dynamoDBClient, logger diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index eb0fb24736..7e6c0963a7 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -25,10 +25,8 @@ import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; import { clientKindToTokenGenerationStatesClientKind, - convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, deleteEntriesFromTokenGenStatesByClientIdKid, - deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, readPlatformClientEntry, deleteClientEntryFromTokenGenerationStates, extractAgreementIdFromAgreementPK, @@ -40,6 +38,7 @@ import { updateTokenGenStatesDataForSecondRetrieval, createTokenGenStatesConsumerClient, deleteEntriesFromTokenGenStatesByClientIdV2, + deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2, } from "./utils.js"; export async function handleMessageV2( @@ -400,10 +399,7 @@ export async function handleMessageV2( ); return Promise.resolve(); } else { - const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ - clientId: client.id, - purposeId: unsafeBrandId(msg.data.purposeId), - }); + const purposeId = unsafeBrandId(msg.data.purposeId); // platform-states await setClientPurposeIdsInPlatformStatesEntry( @@ -413,19 +409,12 @@ export async function handleMessageV2( ); // token-generation-states - if (client.purposes.length > 0) { - await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( - GSIPK_clientId_purposeId, - dynamoDBClient, - logger - ); - } else { - await convertEntriesToClientKidInTokenGenerationStates( - GSIPK_clientId_purposeId, - dynamoDBClient, - logger - ); - } + await deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2( + client, + purposeId, + dynamoDBClient, + logger + ); } } else { logger.info( diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 09ca105436..22bc0da0b6 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -12,6 +12,7 @@ import { QueryCommandOutput, QueryInput, ScanCommand, + ScanCommandOutput, ScanInput, UpdateItemCommand, UpdateItemInput, @@ -208,7 +209,7 @@ export const deleteEntriesFromTokenGenStatesByClientIdV1 = async ( }; export const deleteEntriesFromTokenGenStatesByClientIdV2 = async ( - // For v2 events we have the entire client, so we can build all the PKs we need + // For v2 events we have the entire client, so we can build all the PKs of the entries we need to delete client: Client, dynamoDBClient: DynamoDBClient, logger: Logger @@ -296,25 +297,26 @@ export const readPlatformClientEntry = async ( } }; -const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( +const readTokenGenStatesConsumerClientsByGSIPKClientPurposeV1 = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise<{ - tokenGenStatesEntries: TokenGenStatesConsumerClientGSIClientPurpose[]; + tokenGenStatesEntries: TokenGenerationStatesConsumerClient[]; lastEvaluatedKey: Record | undefined; }> => { - const input: QueryInput = { + // This function performs a Scan because ConsistentRead can't be used on GSIPKs + const readInput: ScanInput = { TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - IndexName: "ClientPurpose", - KeyConditionExpression: `GSIPK_clientId_purposeId = :gsiValue`, + FilterExpression: `GSIPK_clientId_purposeId = :gsiValue`, ExpressionAttributeValues: { ":gsiValue": { S: GSIPK_clientId_purposeId }, }, ExclusiveStartKey: exclusiveStartKey, + ConsistentRead: true, }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); if (!data.Items) { throw genericInternalError( @@ -326,7 +328,7 @@ const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( const unmarshalledItems = data.Items.map((item) => unmarshall(item)); const tokenGenStatesEntries = z - .array(TokenGenStatesConsumerClientGSIClientPurpose) + .array(TokenGenerationStatesConsumerClient) .safeParse(unmarshalledItems); if (!tokenGenStatesEntries.success) { @@ -336,6 +338,7 @@ const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( )} - data ${JSON.stringify(data)} ` ); } + return { tokenGenStatesEntries: tokenGenStatesEntries.data, lastEvaluatedKey: data.LastEvaluatedKey, @@ -343,7 +346,30 @@ const readTokenGenStatesConsumerClientsByGSIPKClientPurpose = async ( } }; -export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( +export const deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2 = async ( + client: Client, + purposeId: PurposeId, + dynamoDBClient: DynamoDBClient, + logger: Logger +): Promise => { + // For v2 events we have the entire client, so we can build all the PKs of the entries we need to delete + await Promise.all( + client.keys.map(async (key) => { + const pk = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key.kid, + purposeId, + }); + await deleteClientEntryFromTokenGenerationStates( + pk, + dynamoDBClient, + logger + ); + }) + ); +}; + +export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1 = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, logger: Logger @@ -353,7 +379,7 @@ export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId = async ( dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { - const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurpose( + const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurposeV1( GSIPK_clientId_purposeId, dynamoDBClient, exclusiveStartKey @@ -388,7 +414,7 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { - const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurpose( + const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurposeV1( GSIPK_clientId_purposeId, dynamoDBClient, exclusiveStartKey diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index f118f11185..bf1ec928d4 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -2800,19 +2800,22 @@ describe("integration tests V2 events", async () => { expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); - it("should update platform-states entry and delete token-generation-states entries for that purpose", async () => { + it("should update platform-states entry and delete token-generation-states entries for that purpose (the removed purposeId wasn't the only one left)", async () => { const previousPlatformEntryVersion = 1; const messageVersion = 2; + const key1 = getMockKey(); + const key2 = getMockKey(); const purposeId1 = generateId(); - const purposeId2 = generateId(); + const removedPurposeId = generateId(); const client: Client = { ...getMockClient(), + keys: [key1, key2], purposes: [purposeId1], }; const payload: ClientPurposeRemovedV2 = { - purposeId: purposeId2, + purposeId: removedPurposeId, client: toClientV2(client), }; const message: AuthorizationEventEnvelope = { @@ -2834,7 +2837,7 @@ describe("integration tests V2 events", async () => { clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), clientConsumerId: client.consumerId, updatedAt: new Date().toISOString(), - clientPurposesIds: [purposeId1, purposeId2], + clientPurposesIds: [purposeId1, removedPurposeId], }; await writePlatformClientEntry( previousPlatformClientEntry, @@ -2843,27 +2846,39 @@ describe("integration tests V2 events", async () => { ); // token-generation-states - const mockClientKidPurpose1kid = "mockClientKidPurpose1kid"; - const mockClientKidPurpose2kid = "mockClientKidPurpose2kid"; const tokenClientKidPurposePK1 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: mockClientKidPurpose1kid, + kid: key1.kid, purposeId: purposeId1, }); const tokenClientKidPurposePK2 = makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, - kid: mockClientKidPurpose2kid, - purposeId: purposeId2, + kid: key1.kid, + purposeId: removedPurposeId, + }); + + const tokenClientKidPurposePK3 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key2.kid, + purposeId: purposeId1, }); + const tokenClientKidPurposePK4 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key2.kid, + purposeId: removedPurposeId, + }); + const gsiPKClientIdPurposeId1 = makeGSIPKClientIdPurposeId({ clientId: client.id, purposeId: purposeId1, }); const gsiPKClientIdPurposeId2 = makeGSIPKClientIdPurposeId({ clientId: client.id, - purposeId: purposeId2, + purposeId: removedPurposeId, }); const tokenClientEntry = getMockTokenGenStatesApiClient(); @@ -2874,7 +2889,7 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: mockClientKidPurpose1kid, + kid: key1.kid, }), GSIPK_purposeId: purposeId1, }; @@ -2885,9 +2900,31 @@ describe("integration tests V2 events", async () => { GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ clientId: client.id, - kid: mockClientKidPurpose2kid, + kid: key1.kid, + }), + GSIPK_purposeId: removedPurposeId, + }; + + const tokenConsumerClient3: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK3), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId1, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key2.kid, }), - GSIPK_purposeId: purposeId2, + GSIPK_purposeId: purposeId1, + }; + + const tokenConsumerClient4: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK4), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId2, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key2.kid, + }), + GSIPK_purposeId: removedPurposeId, }; await writeTokenGenStatesApiClient( @@ -2904,6 +2941,15 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient3, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient4, + dynamoDBClient + ); + await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states @@ -2923,13 +2969,143 @@ describe("integration tests V2 events", async () => { const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toHaveLength(3); expect(retrievedTokenGenStatesEntries).toEqual( - expect.arrayContaining([tokenClientEntry, tokenConsumerClient1]) + expect.arrayContaining([ + tokenClientEntry, + tokenConsumerClient1, + tokenConsumerClient3, + ]) ); }); }); + it("should update platform-states entry and delete token-generation-states entries for that purpose (the removed purposeId was the only one left)", async () => { + const previousPlatformEntryVersion = 1; + const messageVersion = 2; + + const key1 = getMockKey(); + const key2 = getMockKey(); + const removedPurposeId = generateId(); + const client: Client = { + ...getMockClient(), + keys: [key1, key2], + purposes: [], + }; + + const payload: ClientPurposeRemovedV2 = { + purposeId: removedPurposeId, + client: toClientV2(client), + }; + const message: AuthorizationEventEnvelope = { + sequence_num: 1, + stream_id: client.id, + version: messageVersion, + type: "ClientPurposeRemoved", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformClientPK = makePlatformStatesClientPK(client.id); + const previousPlatformClientEntry: PlatformStatesClientEntry = { + PK: platformClientPK, + version: previousPlatformEntryVersion, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + updatedAt: new Date().toISOString(), + clientPurposesIds: [removedPurposeId], + }; + await writePlatformClientEntry( + previousPlatformClientEntry, + dynamoDBClient, + genericLogger + ); + + // token-generation-states + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key1.kid, + purposeId: removedPurposeId, + }); + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key2.kid, + purposeId: removedPurposeId, + }); + + const gsiPKClientIdPurposeId = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: removedPurposeId, + }); + + const tokenClientEntry = getMockTokenGenStatesApiClient(); + + const tokenConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key1.kid, + }), + GSIPK_purposeId: removedPurposeId, + }; + + const tokenConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId: gsiPKClientIdPurposeId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key2.kid, + }), + GSIPK_purposeId: removedPurposeId, + }; + + await writeTokenGenStatesApiClient( + tokenClientEntry, + dynamoDBClient, + genericLogger + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenConsumerClient2, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient, genericLogger); + + // platform-states + const retrievedPlatformStatesEntry = await readPlatformClientEntry( + platformClientPK, + dynamoDBClient + ); + const expectedPlatformStatesEntry: PlatformStatesClientEntry = { + ...previousPlatformClientEntry, + clientPurposesIds: [], + version: messageVersion, + updatedAt: new Date().toISOString(), + }; + expect(retrievedPlatformStatesEntry).toEqual(expectedPlatformStatesEntry); + + // token-generation-states + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + expect(retrievedTokenGenStatesEntries).toHaveLength(1); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([tokenClientEntry]) + ); + }); + describe("ClientDeleted", () => { it("should delete platform-states entry and token-generation-states entries", async () => { const messageVersion = 2; diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 7d8e1e6b4e..88d0b86216 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -81,11 +81,12 @@ import { writePlatformClientEntry, deleteClientEntryFromTokenGenerationStates, readPlatformClientEntry, - deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId, + deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1, upsertTokenGenStatesConsumerClient, upsertTokenGenStatesApiClient, deleteEntriesFromTokenGenStatesByClientIdV2, deleteEntriesFromTokenGenStatesByClientIdV1, + deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2, } from "../src/utils.js"; import { dynamoDBClient } from "./utils.js"; @@ -402,7 +403,7 @@ describe("utils", () => { expect(res).toEqual(clientEntry1); }); - it("deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId", async () => { + it("deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1", async () => { const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ clientId: generateId(), purposeId: generateId(), @@ -432,7 +433,7 @@ describe("utils", () => { dynamoDBClient ); - await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeId( + await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1( GSIPK_clientId_purposeId, dynamoDBClient, genericLogger @@ -442,6 +443,66 @@ describe("utils", () => { expect(result).toEqual([tokenGenStatesConsumerClient3]); }); + it("deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2", async () => { + const purposeId = generateId(); + + const key1 = getMockKey(); + const key2 = getMockKey(); + const client: Client = { ...getMockClient(), keys: [key1, key2] }; + const tokenClientKidPurposePK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key1.kid, + purposeId, + }); + + const tokenClientKidPurposePK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key2.kid, + purposeId, + }); + + const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK1), + GSIPK_clientId_purposeId, + }; + + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenClientKidPurposePK2), + GSIPK_clientId_purposeId, + }; + + const tokenGenStatesConsumerClient3 = getMockTokenGenStatesConsumerClient(); + + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient3, + dynamoDBClient + ); + + await deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2( + client, + purposeId, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([tokenGenStatesConsumerClient3]); + }); + it("convertEntriesToClientKidInTokenGenerationStates", async () => { const clientId = generateId(); const kid1 = "kid1"; From 40c1f05363a9c1bb5102047615c5906fb1b67fbc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 Jan 2025 15:26:05 +0100 Subject: [PATCH 105/126] Fix ClientKeyDeleted in authorization-platformstate-writer (#1357) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 9 +- .../src/consumerServiceV2.ts | 11 +- .../src/utils.ts | 232 ++++++------ .../test/utils.test.ts | 342 ++++++++++++++---- 4 files changed, 399 insertions(+), 195 deletions(-) diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 1a5e74691a..484f3777e9 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -32,7 +32,7 @@ import { createTokenGenStatesConsumerClient, deleteClientEntryFromPlatformStates, deleteClientEntryFromTokenGenerationStates, - deleteEntriesFromTokenGenStatesByClientIdKid, + deleteEntriesFromTokenGenStatesByClientIdKidV1, deleteEntriesFromTokenGenStatesByClientIdV1, deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1, extractAgreementIdFromAgreementPK, @@ -297,12 +297,9 @@ export async function handleMessageV1( logger ); - const GSIPK_clientId_kid = makeGSIPKClientIdKid({ + await deleteEntriesFromTokenGenStatesByClientIdKidV1( clientId, - kid: msg.data.keyId, - }); - await deleteEntriesFromTokenGenStatesByClientIdKid( - GSIPK_clientId_kid, + msg.data.keyId, dynamoDBClient, logger ); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 7e6c0963a7..0a409d217f 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -26,7 +26,6 @@ import { Logger } from "pagopa-interop-commons"; import { clientKindToTokenGenerationStatesClientKind, deleteClientEntryFromPlatformStates, - deleteEntriesFromTokenGenStatesByClientIdKid, readPlatformClientEntry, deleteClientEntryFromTokenGenerationStates, extractAgreementIdFromAgreementPK, @@ -39,6 +38,7 @@ import { createTokenGenStatesConsumerClient, deleteEntriesFromTokenGenStatesByClientIdV2, deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2, + deleteEntriesFromTokenGenStatesByClientIdKidV2, } from "./utils.js"; export async function handleMessageV2( @@ -266,12 +266,9 @@ export async function handleMessageV2( ); } - const GSIPK_clientId_kid = makeGSIPKClientIdKid({ - clientId: client.id, - kid: msg.data.kid, - }); - await deleteEntriesFromTokenGenStatesByClientIdKid( - GSIPK_clientId_kid, + await deleteEntriesFromTokenGenStatesByClientIdKidV2( + client, + msg.data.kid, dynamoDBClient, logger ); diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 22bc0da0b6..9fb3e49e53 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -50,78 +50,134 @@ import { TokenGenerationStatesClientKidPurposePK, TokenGenerationStatesConsumerClient, TokenGenStatesConsumerClientGSIClientPurpose, - TokenGenStatesGenericClientGSIClientKid, Client, TokenGenerationStatesGenericClient, TenantId, makeGSIPKClientIdKid, + clientKidPrefix, + clientKidPurposePrefix, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; import { z } from "zod"; import { config } from "./config/config.js"; -export const deleteEntriesFromTokenGenStatesByClientIdKid = async ( - GSIPK_clientId_kid: GSIPKClientIdKid, +const runPaginatedQueryDeleteClientEntryFromTokenGenStatesByPrefixV1 = async ( + prefix: string, dynamoDBClient: DynamoDBClient, - logger: Logger + logger: Logger, + exclusiveStartKey?: Record ): Promise => { - const runPaginatedQuery = async ( - GSIPK_clientId_kid: GSIPKClientIdKid, - dynamoDBClient: DynamoDBClient, - exclusiveStartKey?: Record - ): Promise => { - const input: QueryInput = { - TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - IndexName: "ClientKid", - KeyConditionExpression: `GSIPK_clientId_kid = :gsiValue`, - ExpressionAttributeValues: { - ":gsiValue": { S: GSIPK_clientId_kid }, - }, - ExclusiveStartKey: exclusiveStartKey, - }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); - if (!data.Items) { + const readInput: ScanInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + FilterExpression: "begins_with(#pk, :prefix)", + ExpressionAttributeNames: { + "#pk": "PK", + }, + ExpressionAttributeValues: { + ":prefix": { S: prefix }, + }, + ExclusiveStartKey: exclusiveStartKey, + ConsistentRead: true, + }; + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenGenStatesEntries = z + .array(TokenGenerationStatesGenericClient) + .safeParse(unmarshalledItems); + + if (!tokenGenStatesEntries.success) { throw genericInternalError( - `Unable to read token-generation-states entries: result ${JSON.stringify( - data - )} ` + `Unable to parse token-generation-states entries: result ${JSON.stringify( + tokenGenStatesEntries + )} - data ${JSON.stringify(data)} ` ); - } else { - const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + } - const tokenGenStatesEntries = z - .array(TokenGenStatesGenericClientGSIClientKid) - .safeParse(unmarshalledItems); + for (const entry of tokenGenStatesEntries.data) { + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient, + logger + ); + } - if (!tokenGenStatesEntries.success) { - throw genericInternalError( - `Unable to parse token-generation-states entries: result ${JSON.stringify( - tokenGenStatesEntries - )} - data ${JSON.stringify(data)} ` - ); - } + if (data.LastEvaluatedKey) { + await runPaginatedQueryDeleteClientEntryFromTokenGenStatesByPrefixV1( + prefix, + dynamoDBClient, + logger, + data.LastEvaluatedKey + ); + } + } +}; + +export const deleteEntriesFromTokenGenStatesByClientIdKidV1 = async ( + clientId: ClientId, + kid: string, + dynamoDBClient: DynamoDBClient, + logger: Logger +): Promise => { + const prefix1 = `${clientKidPrefix}${clientId}#${kid}`; + const prefix2 = `${clientKidPurposePrefix}${clientId}#${kid}`; + + await runPaginatedQueryDeleteClientEntryFromTokenGenStatesByPrefixV1( + prefix1, + dynamoDBClient, + logger, + undefined + ); + await runPaginatedQueryDeleteClientEntryFromTokenGenStatesByPrefixV1( + prefix2, + dynamoDBClient, + logger, + undefined + ); +}; - for (const entry of tokenGenStatesEntries.data) { +export const deleteEntriesFromTokenGenStatesByClientIdKidV2 = async ( + client: Client, + kid: string, + dynamoDBClient: DynamoDBClient, + logger: Logger +): Promise => { + if (client.purposes.length > 0) { + await Promise.all( + client.purposes.map(async (purposeId) => { + const pk = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid, + purposeId, + }); await deleteClientEntryFromTokenGenerationStates( - entry.PK, + pk, dynamoDBClient, logger ); - } - - if (data.LastEvaluatedKey) { - await runPaginatedQuery( - GSIPK_clientId_kid, - dynamoDBClient, - data.LastEvaluatedKey - ); - } - } - }; - - await runPaginatedQuery(GSIPK_clientId_kid, dynamoDBClient, undefined); + }) + ); + } else { + const pk = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid, + }); + await deleteClientEntryFromTokenGenerationStates( + pk, + dynamoDBClient, + logger + ); + } }; export const deleteClientEntryFromPlatformStates = async ( @@ -145,67 +201,23 @@ export const deleteEntriesFromTokenGenStatesByClientIdV1 = async ( dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { - // We need to find all the entries to delete through a Scan, because the query on the GSI doesn't allow ConsistentRead - const runPaginatedQuery = async ( - clientId: ClientId, - dynamoDBClient: DynamoDBClient, - exclusiveStartKey?: Record - ): Promise => { - const readInput: ScanInput = { - TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - FilterExpression: "contains(#pk, :clientId)", - ExpressionAttributeNames: { - "#pk": "PK", - }, - ExpressionAttributeValues: { - ":clientId": { S: clientId }, - }, - ConsistentRead: true, - ExclusiveStartKey: exclusiveStartKey, - }; - const commandQuery = new ScanCommand(readInput); - const data = await dynamoDBClient.send(commandQuery); - - if (!data.Items) { - throw genericInternalError( - `Unable to read token-generation-states entries: result ${JSON.stringify( - data - )} ` - ); - } else { - const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - - const tokenGenStatesEntries = z - .array(TokenGenerationStatesGenericClient) - .safeParse(unmarshalledItems); - - if (!tokenGenStatesEntries.success) { - throw genericInternalError( - `Unable to parse token-generation-states entries: result ${JSON.stringify( - tokenGenStatesEntries - )} - data ${JSON.stringify(data)} ` - ); - } + // We need to find all the entries to delete though a Scan, because the query on the GSI doesn't allow ConsistentRead - for (const entry of tokenGenStatesEntries.data) { - await deleteClientEntryFromTokenGenerationStates( - entry.PK, - dynamoDBClient, - logger - ); - } + const prefix1 = `${clientKidPrefix}${clientId}`; + const prefix2 = `${clientKidPurposePrefix}${clientId}`; - if (data.LastEvaluatedKey) { - await runPaginatedQuery( - clientId, - dynamoDBClient, - data.LastEvaluatedKey - ); - } - } - }; - - await runPaginatedQuery(clientId, dynamoDBClient, undefined); + await runPaginatedQueryDeleteClientEntryFromTokenGenStatesByPrefixV1( + prefix1, + dynamoDBClient, + logger, + undefined + ); + await runPaginatedQueryDeleteClientEntryFromTokenGenStatesByPrefixV1( + prefix2, + dynamoDBClient, + logger, + undefined + ); }; export const deleteEntriesFromTokenGenStatesByClientIdV2 = async ( diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 88d0b86216..0ae784364d 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -77,7 +77,7 @@ import { updateTokenGenStatesDataForSecondRetrieval, upsertPlatformClientEntry, writeTokenGenStatesApiClient, - deleteEntriesFromTokenGenStatesByClientIdKid, + deleteEntriesFromTokenGenStatesByClientIdKidV1, writePlatformClientEntry, deleteClientEntryFromTokenGenerationStates, readPlatformClientEntry, @@ -87,6 +87,7 @@ import { deleteEntriesFromTokenGenStatesByClientIdV2, deleteEntriesFromTokenGenStatesByClientIdV1, deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2, + deleteEntriesFromTokenGenStatesByClientIdKidV2, } from "../src/utils.js"; import { dynamoDBClient } from "./utils.js"; @@ -106,47 +107,177 @@ describe("utils", () => { vi.useRealTimers(); }); - it("deleteEntriesFromTokenGenStatesByClientIdKid", async () => { - const clientIdkid = makeGSIPKClientIdKid({ - clientId: generateId(), - kid: "kid", + describe("deleteEntriesFromTokenGenStatesByClientIdKidV1", () => { + it("ApiClient", async () => { + const clientId = generateId(); + const kid = "kid"; + const clientIdkid = makeGSIPKClientIdKid({ + clientId, + kid, + }); + + const apiClientPK = makeTokenGenerationStatesClientKidPK({ + clientId, + kid, + }); + const clientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(apiClientPK), + GSIPK_clientId_kid: clientIdkid, + }; + + const otherApiClient = getMockTokenGenStatesApiClient(); + + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); + + await writeTokenGenStatesApiClient( + otherApiClient, + dynamoDBClient, + genericLogger + ); + + await deleteEntriesFromTokenGenStatesByClientIdKidV1( + clientId, + kid, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherApiClient]); }); - const clientEntry: TokenGenerationStatesApiClient = { - ...getMockTokenGenStatesApiClient(), - GSIPK_clientId_kid: clientIdkid, - }; - const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = { - ...getMockTokenGenStatesConsumerClient(), - GSIPK_clientId_kid: clientIdkid, - }; + it("ConsumerClient", async () => { + const clientId = generateId(); + const kid = "kid"; + const purposeId = generateId(); - const otherConsumerClient: TokenGenerationStatesConsumerClient = { - ...getMockTokenGenStatesConsumerClient(), - }; + const consumerClientPK = makeTokenGenerationStatesClientKidPurposePK({ + clientId, + kid, + purposeId, + }); + const consumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(consumerClientPK), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId, + kid, + }), + }; - await writeTokenGenStatesApiClient( - clientEntry, - dynamoDBClient, - genericLogger - ); - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient, - dynamoDBClient - ); - await writeTokenGenStatesConsumerClient( - otherConsumerClient, - dynamoDBClient - ); + const otherConsumerClient = getMockTokenGenStatesConsumerClient(); - await deleteEntriesFromTokenGenStatesByClientIdKid( - clientIdkid, - dynamoDBClient, - genericLogger - ); + await writeTokenGenStatesConsumerClient(consumerClient, dynamoDBClient); - const result = await readAllTokenGenStatesItems(dynamoDBClient); - expect(result).toEqual([otherConsumerClient]); + await writeTokenGenStatesConsumerClient( + otherConsumerClient, + dynamoDBClient + ); + + await deleteEntriesFromTokenGenStatesByClientIdKidV1( + clientId, + kid, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherConsumerClient]); + }); + }); + + describe("deleteEntriesFromTokenGenStatesByClientIdKidV2", () => { + it("ApiClient", async () => { + const key = getMockKey(); + + const client: Client = { + ...getMockClient(), + keys: [key], + }; + const clientIdkid = makeGSIPKClientIdKid({ + clientId: client.id, + kid: key.kid, + }); + + const apiClientPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }); + const clientEntry: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(apiClientPK), + GSIPK_clientId_kid: clientIdkid, + }; + + const otherApiClient = getMockTokenGenStatesApiClient(); + + await writeTokenGenStatesApiClient( + clientEntry, + dynamoDBClient, + genericLogger + ); + + await writeTokenGenStatesApiClient( + otherApiClient, + dynamoDBClient, + genericLogger + ); + + await deleteEntriesFromTokenGenStatesByClientIdKidV2( + client, + key.kid, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherApiClient]); + }); + + it("ConsumerClient", async () => { + const key = getMockKey(); + const purposeId = generateId(); + + const client: Client = { + ...getMockClient(), + keys: [key], + purposes: [purposeId], + }; + + const consumerClientPK = makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key.kid, + purposeId, + }); + const consumerClient: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(consumerClientPK), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: key.kid, + }), + }; + + const otherConsumerClient = getMockTokenGenStatesConsumerClient(); + + await writeTokenGenStatesConsumerClient(consumerClient, dynamoDBClient); + + await writeTokenGenStatesConsumerClient( + otherConsumerClient, + dynamoDBClient + ); + + await deleteEntriesFromTokenGenStatesByClientIdKidV2( + client, + key.kid, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherConsumerClient]); + }); }); it("deleteClientEntryFromPlatformStates", async () => { @@ -174,47 +305,114 @@ describe("utils", () => { expect(res).toEqual([clientEntry2]); }); - it("deleteEntriesFromTokenGenStatesByClientIdV1 - ApiClient", async () => { - const mockKey1 = getMockKey(); - const mockKey2 = getMockKey(); + describe("deleteEntriesFromTokenGenStatesByClientIdV1", () => { + it("ApiClient", async () => { + const mockKey1 = getMockKey(); + const mockKey2 = getMockKey(); - const client: Client = { - ...getMockClient(), - keys: [mockKey1, mockKey2], - purposes: [], - }; + const client: Client = { + ...getMockClient(), + keys: [mockKey1, mockKey2], + purposes: [], + }; - const apiClient: TokenGenerationStatesApiClient = { - ...getMockTokenGenStatesApiClient(), - PK: makeTokenGenerationStatesClientKidPK({ - clientId: client.id, - kid: mockKey1.kid, - }), - GSIPK_clientId: client.id, - }; + const apiClient1: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: mockKey1.kid, + }), + GSIPK_clientId: client.id, + }; - const otherApiClient: TokenGenerationStatesApiClient = { - ...getMockTokenGenStatesApiClient(), - }; - await writeTokenGenStatesApiClient( - apiClient, - dynamoDBClient, - genericLogger - ); - await writeTokenGenStatesApiClient( - otherApiClient, - dynamoDBClient, - genericLogger - ); + const apiClient2: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + PK: makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: mockKey2.kid, + }), + GSIPK_clientId: client.id, + }; - await deleteEntriesFromTokenGenStatesByClientIdV1( - client.id, - dynamoDBClient, - genericLogger - ); + const otherApiClient: TokenGenerationStatesApiClient = { + ...getMockTokenGenStatesApiClient(), + }; + await writeTokenGenStatesApiClient( + apiClient1, + dynamoDBClient, + genericLogger + ); + await writeTokenGenStatesApiClient( + apiClient2, + dynamoDBClient, + genericLogger + ); + await writeTokenGenStatesApiClient( + otherApiClient, + dynamoDBClient, + genericLogger + ); - const result = await readAllTokenGenStatesItems(dynamoDBClient); - expect(result).toEqual([otherApiClient]); + await deleteEntriesFromTokenGenStatesByClientIdV1( + client.id, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherApiClient]); + }); + + it("ConsumerClient", async () => { + const mockKey1 = getMockKey(); + const mockKey2 = getMockKey(); + const purposeId = generateId(); + + const client: Client = { + ...getMockClient(), + keys: [mockKey1, mockKey2], + purposes: [purposeId], + }; + + const consumerClient1: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockKey1.kid, + purposeId, + }), + GSIPK_clientId: client.id, + }; + + const consumerClient2: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(), + PK: makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockKey2.kid, + purposeId, + }), + GSIPK_clientId: client.id, + }; + + const otherConsumerClient = getMockTokenGenStatesConsumerClient(); + + await writeTokenGenStatesConsumerClient(consumerClient1, dynamoDBClient); + await writeTokenGenStatesConsumerClient(consumerClient2, dynamoDBClient); + + await writeTokenGenStatesConsumerClient( + otherConsumerClient, + dynamoDBClient + ); + + await deleteEntriesFromTokenGenStatesByClientIdV1( + client.id, + dynamoDBClient, + genericLogger + ); + + const result = await readAllTokenGenStatesItems(dynamoDBClient); + expect(result).toEqual([otherConsumerClient]); + }); }); describe("deleteEntriesFromTokenGenStatesByClientIdV2", () => { From 71359346f14a5d4b614b3e9e58f154561102bd17 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Mon, 13 Jan 2025 15:50:02 +0100 Subject: [PATCH 106/126] Fix ClientKeyAdded in authorization-platformstate-writer (#1360) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- .../src/consumerServiceV2.ts | 212 ++++++++---------- .../consumerServiceV2.integration.test.ts | 62 ++--- 2 files changed, 112 insertions(+), 162 deletions(-) diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index 0a409d217f..a857e1dee5 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -90,130 +90,112 @@ export async function handleMessageV2( // token-generation-states await match(client.kind) .with(clientKind.consumer, async () => { - if (client.purposes.length > 0) { - const addedEntries = await Promise.all( - client.purposes.map(async (purposeId) => { - const { purposeEntry, agreementEntry, catalogEntry } = - await retrievePlatformStatesByPurpose( - purposeId, - dynamoDBClient, - logger - ); - - const tokenClientKidPurposePK = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: client.id, - kid: msg.data.kid, - purposeId, - }); - - const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = - { - PK: tokenClientKidPurposePK, - consumerId: client.consumerId, - clientKind: clientKindTokenGenStates.consumer, - publicKey: pem, - updatedAt: new Date().toISOString(), - GSIPK_clientId: client.id, - GSIPK_clientId_kid: makeGSIPKClientIdKid({ - clientId: client.id, - kid: msg.data.kid, - }), - GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ - clientId: client.id, - purposeId, - }), - GSIPK_purposeId: purposeId, - ...(purposeEntry - ? { - GSIPK_consumerId_eserviceId: - makeGSIPKConsumerIdEServiceId({ - consumerId: client.consumerId, - eserviceId: purposeEntry.purposeEserviceId, - }), - purposeState: purposeEntry.state, - purposeVersionId: purposeEntry.purposeVersionId, - } - : {}), - ...(purposeEntry && agreementEntry - ? { - agreementId: extractAgreementIdFromAgreementPK( - agreementEntry.PK - ), - agreementState: agreementEntry.state, - GSIPK_eserviceId_descriptorId: - makeGSIPKEServiceIdDescriptorId({ - eserviceId: purposeEntry.purposeEserviceId, - descriptorId: - agreementEntry.agreementDescriptorId, - }), - } - : {}), - ...(catalogEntry - ? { - descriptorState: catalogEntry.state, - descriptorAudience: catalogEntry.descriptorAudience, - descriptorVoucherLifespan: - catalogEntry.descriptorVoucherLifespan, - } - : {}), - }; - await upsertTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient, - dynamoDBClient, - logger - ); - return tokenGenStatesConsumerClient; - }) + if (client.purposes.length === 0) { + logger.info( + "Token-generation-states. No entry to add because the client doesn't have any purposes yet" ); + return Promise.resolve(); + } - // Second check for updated fields - await Promise.all( - client.purposes.map(async (purposeId, index) => { - const { - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - } = await retrievePlatformStatesByPurpose( + const addedEntries = await Promise.all( + client.purposes.map(async (purposeId) => { + const { purposeEntry, agreementEntry, catalogEntry } = + await retrievePlatformStatesByPurpose( purposeId, dynamoDBClient, logger ); - const addedTokenGenStatesConsumerClient = addedEntries[index]; - await updateTokenGenStatesDataForSecondRetrieval({ - dynamoDBClient, - entry: addedTokenGenStatesConsumerClient, - purposeEntry: purposeEntry2, - agreementEntry: agreementEntry2, - catalogEntry: catalogEntry2, - logger, - }); - }) - ); - } else { - const tokenGenStatesConsumerClientWithoutPurpose: TokenGenerationStatesConsumerClient = - { - PK: makeTokenGenerationStatesClientKidPK({ + const tokenClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ clientId: client.id, kid: msg.data.kid, - }), - consumerId: client.consumerId, - clientKind: clientKindTokenGenStates.consumer, - publicKey: pem, - GSIPK_clientId: client.id, - GSIPK_clientId_kid: makeGSIPKClientIdKid({ - clientId: client.id, - kid: msg.data.kid, - }), - updatedAt: new Date().toISOString(), - }; - await upsertTokenGenStatesConsumerClient( - tokenGenStatesConsumerClientWithoutPurpose, - dynamoDBClient, - logger - ); - } + purposeId, + }); + + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenClientKidPurposePK, + consumerId: client.consumerId, + clientKind: clientKindTokenGenStates.consumer, + publicKey: pem, + updatedAt: new Date().toISOString(), + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: msg.data.kid, + }), + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId, + }), + GSIPK_purposeId: purposeId, + ...(purposeEntry + ? { + GSIPK_consumerId_eserviceId: + makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purposeEntry.purposeEserviceId, + }), + purposeState: purposeEntry.state, + purposeVersionId: purposeEntry.purposeVersionId, + } + : {}), + ...(purposeEntry && agreementEntry + ? { + agreementId: extractAgreementIdFromAgreementPK( + agreementEntry.PK + ), + agreementState: agreementEntry.state, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: purposeEntry.purposeEserviceId, + descriptorId: agreementEntry.agreementDescriptorId, + }), + } + : {}), + ...(catalogEntry + ? { + descriptorState: catalogEntry.state, + descriptorAudience: catalogEntry.descriptorAudience, + descriptorVoucherLifespan: + catalogEntry.descriptorVoucherLifespan, + } + : {}), + }; + await upsertTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient, + logger + ); + return tokenGenStatesConsumerClient; + }) + ); + + // Second check for updated fields + await Promise.all( + client.purposes.map(async (purposeId, index) => { + const { + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + } = await retrievePlatformStatesByPurpose( + purposeId, + dynamoDBClient, + logger + ); + + const addedTokenGenStatesConsumerClient = addedEntries[index]; + await updateTokenGenStatesDataForSecondRetrieval({ + dynamoDBClient, + entry: addedTokenGenStatesConsumerClient, + purposeEntry: purposeEntry2, + agreementEntry: agreementEntry2, + catalogEntry: catalogEntry2, + logger, + }); + }) + ); }) .with(clientKind.api, async () => { const tokenGenStatesApiClient: TokenGenerationStatesApiClient = { diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index bf1ec928d4..22257b7c93 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -61,6 +61,7 @@ import { toClientV2, TokenGenerationStatesApiClient, TokenGenerationStatesConsumerClient, + clientKind, } from "pagopa-interop-models"; import { genericLogger } from "pagopa-interop-commons"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; @@ -166,7 +167,7 @@ describe("integration tests V2 events", async () => { expect(retrievedTokenGenStatesEntries).toEqual([tokenClientEntry]); }); - it("should insert platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { + it("ConsumerClient - should insert platform-states entry and insert token-generation-states client-kid-purpose entries if the client contains at least one purpose", async () => { const messageVersion = 2; const consumerId = generateId(); @@ -455,7 +456,7 @@ describe("integration tests V2 events", async () => { ); }); - it("should update platform-states entry and insert token-generation-states client-kid-purpose entries", async () => { + it("ConsumerClient - should update platform-states entry and insert token-generation-states client-kid-purpose entries", async () => { const previousPlatformEntryVersion = 1; const messageVersion = 2; @@ -753,7 +754,7 @@ describe("integration tests V2 events", async () => { ); }); - it("should update platform-states entry and update token-generation-states client-kid-purpose entries", async () => { + it("ConsumerClient - should update platform-states entry and update token-generation-states client-kid-purpose entries", async () => { const previousPlatformEntryVersion = 1; const messageVersion = 2; @@ -1103,7 +1104,7 @@ describe("integration tests V2 events", async () => { ); }); - it("should insert platform-states entry and insert token-generation-states client-kid entry if the client does not contain purposes", async () => { + it("ConsumerClient - should insert platform-states entry and add no entries in token-generation-states if the client does not contain purposes", async () => { const messageVersion = 1; const oldKey = getMockKey(); @@ -1133,26 +1134,6 @@ describe("integration tests V2 events", async () => { await readPlatformClientEntry(platformClientPK, dynamoDBClient) ).toBeUndefined(); - // token-generation-states - const tokenClientKidPK = makeTokenGenerationStatesClientKidPK({ - clientId: client.id, - kid: oldKey.kid, - }); - const tokenClientEntry: TokenGenerationStatesApiClient = { - ...getMockTokenGenStatesApiClient(tokenClientKidPK), - consumerId: client.consumerId, - GSIPK_clientId: client.id, - GSIPK_clientId_kid: makeGSIPKClientIdKid({ - clientId: client.id, - kid: oldKey.kid, - }), - }; - await writeTokenGenStatesApiClient( - tokenClientEntry, - dynamoDBClient, - genericLogger - ); - await handleMessageV2(message, dynamoDBClient, genericLogger); // platform-states @@ -1175,29 +1156,14 @@ describe("integration tests V2 events", async () => { const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { - PK: makeTokenGenerationStatesClientKidPK({ - clientId: client.id, - kid: addedKey.kid, - }), - consumerId: client.consumerId, - clientKind: clientKindTokenGenStates.consumer, - publicKey: addedKey.encodedPem, - GSIPK_clientId: client.id, - GSIPK_clientId_kid: makeGSIPKClientIdKid({ - clientId: client.id, - kid: addedKey.kid, - }), - updatedAt: new Date().toISOString(), - }; - expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toHaveLength(0); expect(retrievedTokenGenStatesEntries).toEqual( - expect.arrayContaining([tokenClientEntry, expectedTokenClientEntry]) + expect.arrayContaining([]) ); }); - it("should update platform-states entry and insert token-generation-states client-kid entry", async () => { + it("ApiClient - should update platform-states entry and insert token-generation-states client-kid entry", async () => { const previousPlatformEntryVersion = 1; const messageVersion = 2; @@ -1205,6 +1171,7 @@ describe("integration tests V2 events", async () => { const addedKey = getMockKey(); const client: Client = { ...getMockClient(), + kind: clientKind.api, keys: [oldKey, addedKey], }; @@ -1278,13 +1245,13 @@ describe("integration tests V2 events", async () => { const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { + const expectedTokenClientEntry: TokenGenerationStatesApiClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindTokenGenStates.consumer, + clientKind: clientKindTokenGenStates.api, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ @@ -1300,7 +1267,7 @@ describe("integration tests V2 events", async () => { ); }); - it("should update platform-states entry and update token-generation-states client-kid entry", async () => { + it("ApiClient - should update platform-states entry and update token-generation-states client-kid entry", async () => { const previousPlatformEntryVersion = 1; const messageVersion = 2; @@ -1308,6 +1275,7 @@ describe("integration tests V2 events", async () => { const addedKey = getMockKey(); const client: Client = { ...getMockClient(), + kind: clientKind.api, keys: [oldKey, addedKey], }; @@ -1398,13 +1366,13 @@ describe("integration tests V2 events", async () => { const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); - const expectedTokenClientEntry: TokenGenerationStatesConsumerClient = { + const expectedTokenClientEntry: TokenGenerationStatesApiClient = { PK: makeTokenGenerationStatesClientKidPK({ clientId: client.id, kid: addedKey.kid, }), consumerId: client.consumerId, - clientKind: clientKindTokenGenStates.consumer, + clientKind: clientKindTokenGenStates.api, publicKey: addedKey.encodedPem, GSIPK_clientId: client.id, GSIPK_clientId_kid: makeGSIPKClientIdKid({ From fa1a476187c076e731753e48aeb825138ed167d5 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:27:03 +0100 Subject: [PATCH 107/126] PIN-5405: fix agreement-platformstate-writer for pending agreement upgrade (#1289) --- .../src/consumerServiceV1.ts | 130 +----- .../src/consumerServiceV2.ts | 136 +----- .../src/utils.ts | 84 ++++ .../consumerServiceV1.integration.test.ts | 416 ++++++++++++++++++ .../consumerServiceV2.integration.test.ts | 120 +++++ .../src/consumerServiceV1.ts | 4 +- .../src/consumerServiceV2.ts | 4 +- 7 files changed, 660 insertions(+), 234 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 63402072c3..8623227220 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -6,12 +6,9 @@ import { genericInternalError, fromAgreementV1, makeGSIPKConsumerIdEServiceId, - makeGSIPKEServiceIdDescriptorId, makePlatformStatesAgreementPK, - makePlatformStatesEServiceDescriptorPK, PlatformStatesAgreementEntry, agreementState, - PlatformStatesCatalogEntry, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { Logger } from "pagopa-interop-commons"; @@ -21,10 +18,9 @@ import { agreementStateToItemState, updateAgreementStateOnTokenGenStates, writeAgreementEntry, - readCatalogEntry, - updateAgreementStateAndDescriptorInfoOnTokenGenStates, deleteAgreementEntry, isLatestAgreement, + updateLatestAgreementOnTokenGenStates, extractAgreementTimestamp, } from "./utils.js"; @@ -51,9 +47,7 @@ export async function handleMessageV1( const agreement = parseAgreement(msg.data.agreement); await match(agreement.state) - // eslint-disable-next-line sonarjs/no-identical-functions .with(agreementState.active, agreementState.suspended, async () => { - const agreement = parseAgreement(msg.data.agreement); await handleActivationOrSuspension( agreement, dynamoDBClient, @@ -62,7 +56,6 @@ export async function handleMessageV1( ); }) .with(agreementState.archived, async () => { - const agreement = parseAgreement(msg.data.agreement); await handleArchiving(agreement, dynamoDBClient, logger); }) .with( @@ -83,10 +76,8 @@ export async function handleMessageV1( const agreement = parseAgreement(msg.data.agreement); await match(agreement.state) - // eslint-disable-next-line sonarjs/no-identical-functions .with(agreementState.active, agreementState.suspended, async () => { // this case is for agreement upgraded - const agreement = parseAgreement(msg.data.agreement); await handleUpgrade(agreement, dynamoDBClient, msg.version, logger); }) .with( @@ -95,18 +86,20 @@ export async function handleMessageV1( agreementState.missingCertifiedAttributes, agreementState.pending, agreementState.rejected, - // eslint-disable-next-line sonarjs/no-identical-functions () => { logger.info( - `Skipping processing of entry ${agreement.id}. Reason: state ${agreement.state}` + `Skipping processing of agreement ${agreement.id}. Reason: state ${agreement.state}` ); return Promise.resolve(); } ) .exhaustive(); }) + .with({ type: "AgreementDeactivated" }, async (msg) => { + const agreement = parseAgreement(msg.data.agreement); + await handleArchiving(agreement, dynamoDBClient, logger); + }) .with( - { type: "AgreementDeactivated" }, { type: "AgreementDeleted" }, { type: "VerifiedAttributeUpdated" }, { type: "AgreementConsumerDocumentAdded" }, @@ -146,7 +139,6 @@ const handleActivationOrSuspension = async ( if (existingAgreementEntry) { if (existingAgreementEntry.version > incomingVersion) { - // Stops processing if the message is older than the agreement entry logger.info( `Skipping processing of entry ${existingAgreementEntry}. Reason: a more recent entry already exists` ); @@ -180,40 +172,17 @@ const handleActivationOrSuspension = async ( await writeAgreementEntry(agreementEntry, dynamoDBClient, logger); } - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) - ) { - const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); - - const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - - // token-generation-states - await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ - GSIPK_consumerId_eserviceId, - agreementId: agreement.id, - agreementState: agreement.state, - dynamoDBClient, - GSIPK_eserviceId_descriptorId, - catalogEntry, - logger, - }); - } else { - logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` - ); - } + // token-generation-states + /* + In consumerServiceV2, the handler for activation and suspension doesn't have to update + the descriptor info in the token-generation-states. Here it's needed because this handler also + includes the agreement upgrade (which requires updating descriptor info). + */ + await updateLatestAgreementOnTokenGenStates( + dynamoDBClient, + agreement, + logger + ); }; const handleArchiving = async ( @@ -238,7 +207,6 @@ const handleArchiving = async ( ) ) { // token-generation-states only if agreement is the latest - await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreement.state, @@ -263,12 +231,6 @@ const handleUpgrade = async ( const primaryKey = makePlatformStatesAgreementPK(agreement.id); const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); - const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const catalogEntry = await readCatalogEntry(pkCatalogEntry, dynamoDBClient); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, @@ -311,63 +273,9 @@ const handleUpgrade = async ( await writeAgreementEntry(newAgreementEntry, dynamoDBClient, logger); } - const updateLatestAgreementOnTokenGenStates = async ( - catalogEntry: PlatformStatesCatalogEntry | undefined, - agreementTimestamp: string, - logger: Logger - ): Promise => { - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) - ) { - // token-generation-states only if agreement is the latest - const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - - await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ - GSIPK_consumerId_eserviceId, - agreementId: agreement.id, - agreementState: agreement.state, - dynamoDBClient, - GSIPK_eserviceId_descriptorId, - catalogEntry, - logger, - }); - } else { - logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` - ); - } - }; - await updateLatestAgreementOnTokenGenStates( - catalogEntry, - agreementTimestamp, + dynamoDBClient, + agreement, logger ); - - const secondRetrievalCatalogEntry = await readCatalogEntry( - pkCatalogEntry, - dynamoDBClient - ); - if ( - secondRetrievalCatalogEntry && - (!catalogEntry || secondRetrievalCatalogEntry.state !== catalogEntry.state) - ) { - await updateLatestAgreementOnTokenGenStates( - secondRetrievalCatalogEntry, - agreementTimestamp, - logger - ); - } else { - logger.info( - `Token-generation-states. Second retrieval of catalog entry ${pkCatalogEntry} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` - ); - } }; diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index 505bebcdf6..a447671095 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -2,15 +2,13 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { Agreement, AgreementEventEnvelopeV2, + agreementState, AgreementV2, fromAgreementV2, genericInternalError, makeGSIPKConsumerIdEServiceId, - makeGSIPKEServiceIdDescriptorId, makePlatformStatesAgreementPK, - makePlatformStatesEServiceDescriptorPK, PlatformStatesAgreementEntry, - PlatformStatesCatalogEntry, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; @@ -18,12 +16,11 @@ import { agreementStateToItemState, deleteAgreementEntry, readAgreementEntry, - readCatalogEntry, updateAgreementStateInPlatformStatesEntry, updateAgreementStateOnTokenGenStates, - updateAgreementStateAndDescriptorInfoOnTokenGenStates, writeAgreementEntry, isLatestAgreement, + updateLatestAgreementOnTokenGenStates, extractAgreementTimestamp, } from "./utils.js"; @@ -46,11 +43,8 @@ export async function handleMessageV2( eserviceId: agreement.eserviceId, }); - const agreementTimestamp = extractAgreementTimestamp(agreement); - if (existingAgreementEntry) { if (existingAgreementEntry.version > msg.version) { - // Stops processing if the message is older than the agreement entry logger.info( `Skipping processing of entry ${existingAgreementEntry}. Reason: a more recent entry already exists` ); @@ -84,43 +78,11 @@ export async function handleMessageV2( await writeAgreementEntry(agreementEntry, dynamoDBClient, logger); } - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) - ) { - const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const catalogEntry = await readCatalogEntry( - pkCatalogEntry, - dynamoDBClient - ); - - const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - - // token-generation-states - await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ - GSIPK_consumerId_eserviceId, - agreementId: agreement.id, - agreementState: agreement.state, - dynamoDBClient, - GSIPK_eserviceId_descriptorId, - catalogEntry, - logger, - }); - } else { - logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` - ); - } + await updateLatestAgreementOnTokenGenStates( + dynamoDBClient, + agreement, + logger + ); }) .with( { type: "AgreementUnsuspendedByProducer" }, @@ -171,7 +133,6 @@ export async function handleMessageV2( ) ) { // token-generation-states only if agreement is the latest - await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreement.state, @@ -194,11 +155,6 @@ export async function handleMessageV2( dynamoDBClient ); - const pkCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, @@ -207,7 +163,15 @@ export async function handleMessageV2( if (agreementEntry) { if (agreementEntry.version > msg.version) { logger.info( - `Skipping processing of entry ${agreementEntry}. Reason: a more recent entry already exists` + `Skipping processing of entry ${agreementEntry.PK}. Reason: a more recent entry already exists` + ); + return Promise.resolve(); + } else if ( + agreement.state !== agreementState.active && + agreement.state !== agreementState.suspended + ) { + logger.info( + `Skipping processing of entry ${agreementEntry.PK}. Reason: the agreement state ${agreement.state} is not active or suspended` ); return Promise.resolve(); } else { @@ -238,74 +202,11 @@ export async function handleMessageV2( await writeAgreementEntry(newAgreementEntry, dynamoDBClient, logger); } - const updateLatestAgreementOnTokenGenStates = async ( - catalogEntry: PlatformStatesCatalogEntry | undefined, - agreementTimestamp: string, - logger: Logger - ): Promise => { - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) - ) { - // token-generation-states only if agreement is the latest - const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId( - { - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - } - ); - - await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ - GSIPK_consumerId_eserviceId, - agreementId: agreement.id, - agreementState: agreement.state, - dynamoDBClient, - GSIPK_eserviceId_descriptorId, - catalogEntry, - logger, - }); - } else { - logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` - ); - } - }; - - const catalogEntry = await readCatalogEntry( - pkCatalogEntry, - dynamoDBClient - ); - - const agreementTimestamp = extractAgreementTimestamp(agreement); await updateLatestAgreementOnTokenGenStates( - catalogEntry, - agreementTimestamp, + dynamoDBClient, + agreement, logger ); - - const updatedCatalogEntry = await readCatalogEntry( - pkCatalogEntry, - dynamoDBClient - ); - - if ( - updatedCatalogEntry && - (!catalogEntry || updatedCatalogEntry.state !== catalogEntry.state) - ) { - await updateLatestAgreementOnTokenGenStates( - updatedCatalogEntry, - agreementTimestamp, - logger - ); - } else { - logger.info( - `Token-generation-states. Second retrieval of catalog entry ${pkCatalogEntry} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` - ); - } }) .with({ type: "AgreementArchivedByUpgrade" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); @@ -332,7 +233,6 @@ export async function handleMessageV2( ) ) { // token-generation-states only if agreement is the latest - await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, agreementState: agreement.state, diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index e4523a4d58..5d7fd7c7e1 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -14,6 +14,9 @@ import { PlatformStatesAgreementGSIAgreement, TokenGenStatesConsumerClientGSIAgreement, Agreement, + makePlatformStatesEServiceDescriptorPK, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKConsumerIdEServiceId, } from "pagopa-interop-models"; import { AttributeValue, @@ -600,6 +603,87 @@ export const isLatestAgreement = async ( ); }; +export const updateLatestAgreementOnTokenGenStates = async ( + dynamoDBClient: DynamoDBClient, + agreement: Agreement, + logger: Logger +): Promise => { + const processAgreementUpdateOnTokenGenStates = async ( + platformStatesCatalogEntry: PlatformStatesCatalogEntry | undefined, + GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId + ): Promise => { + const currentAgreementTimestamp = extractAgreementTimestamp(agreement); + if ( + await isLatestAgreement( + GSIPK_consumerId_eserviceId, + agreement.id, + currentAgreementTimestamp, + dynamoDBClient + ) + ) { + // token-generation-states only if agreement is the latest + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ + GSIPK_consumerId_eserviceId, + agreementId: agreement.id, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry: platformStatesCatalogEntry, + logger, + }); + } else { + logger.info( + `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + ); + } + }; + + const platformsStatesCatalogEntryPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const platformStatesCatalogEntry = await readCatalogEntry( + platformsStatesCatalogEntryPK, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + + await processAgreementUpdateOnTokenGenStates( + platformStatesCatalogEntry, + GSIPK_consumerId_eserviceId + ); + + // Second check + const updatedPlatformStatesCatalogEntry = await readCatalogEntry( + platformsStatesCatalogEntryPK, + dynamoDBClient + ); + + if ( + updatedPlatformStatesCatalogEntry && + (!platformStatesCatalogEntry || + updatedPlatformStatesCatalogEntry.state !== + platformStatesCatalogEntry.state) + ) { + await processAgreementUpdateOnTokenGenStates( + updatedPlatformStatesCatalogEntry, + GSIPK_consumerId_eserviceId + ); + } else { + logger.info( + `Token-generation-states. Second retrieval of catalog entry ${platformsStatesCatalogEntryPK} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` + ); + } +}; + export const extractAgreementTimestamp = (agreement: Agreement): string => agreement.stamps.upgrade?.when.toISOString() || agreement.stamps.activation?.when.toISOString() || diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index a5cb1d4490..2895ea9088 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -14,6 +14,7 @@ import { Agreement, AgreementActivatedV1, AgreementAddedV1, + AgreementDeactivatedV1, AgreementEventEnvelope, AgreementUpdatedV1, EServiceId, @@ -835,6 +836,126 @@ describe("integration tests V1 events", async () => { ]) ); }); + + it("should do no operation if the agreement state is not active nor suspended", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.pending, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementAddedV1 = { + agreement: toAgreementV1(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 2, + type: "AgreementAdded", + event_version: 1, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformStatesAgreementEntryPK = makePlatformStatesAgreementPK( + agreement.id + ); + const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry(platformStatesAgreementEntryPK), + version: 1, + }; + await writeAgreementEntry( + platformStatesAgreementEntry, + dynamoDBClient, + genericLogger + ); + + const platformStatesCatalogEntryPK = + makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { + PK: platformStatesCatalogEntryPK, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writePlatformCatalogEntry( + platformStatesCatalogEntry, + dynamoDBClient + ); + + // token-generation-states + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient, genericLogger); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + platformStatesAgreementEntryPK, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(platformStatesAgreementEntry); + + // token-generation-states + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -2314,4 +2435,299 @@ describe("integration tests V1 events", async () => { ); }); }); + + describe("AgreementDeactivated (archived by consumer or by upgrade)", () => { + it("agreement is the latest (includes operation on token-generation-states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + archiving: { + when: new Date(), + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementDeactivatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementDeactivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + + // token-generation-states + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient, genericLogger); + + const retrievedEntry = await readAgreementEntry( + latestAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient2, + agreementState: itemState.inactive, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, + ]) + ); + }); + it("agreement is not the latest (no operation on token-generation-states)", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementDeactivatedV1 = { + agreement: toAgreementV1(previousAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: previousAgreement.id, + version: 1, + type: "AgreementDeactivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + previousAgreement.id + ); + const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( + latestAgreement.id + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + previousAgreementEntryPrimaryKey + ), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + previousAgreement.stamps.activation!.when.toISOString(), + }; + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), + state: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSISK_agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + }; + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await writeAgreementEntry( + latestAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + + // token-generation-states + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + agreementId: previousAgreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient, genericLogger); + + const retrievedEntry = await readAgreementEntry( + previousAgreementEntryPrimaryKey, + dynamoDBClient + ); + expect(retrievedEntry).toBeUndefined(); + + // token-generation-states + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient2, + tokenGenStatesConsumerClient1, + ]) + ); + }); + }); }); diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index 117c7c2057..88e01adbdc 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -2564,6 +2564,126 @@ describe("integration tests V2 events", async () => { ]) ); }); + + it("should do no operation if the agreement state is not active nor suspended", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.pending, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementUpgradedV2 = { + agreement: toAgreementV2(agreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: agreement.id, + version: 2, + type: "AgreementUpgraded", + event_version: 2, + data: payload, + log_date: new Date(), + }; + + // platform-states + const platformStatesAgreementEntryPK = makePlatformStatesAgreementPK( + agreement.id + ); + const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry(platformStatesAgreementEntryPK), + version: 1, + }; + await writeAgreementEntry( + platformStatesAgreementEntry, + dynamoDBClient, + genericLogger + ); + + const platformStatesCatalogEntryPK = + makePlatformStatesEServiceDescriptorPK({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { + PK: platformStatesCatalogEntryPK, + state: itemState.active, + descriptorAudience: ["pagopa.it"], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + + await writePlatformCatalogEntry( + platformStatesCatalogEntry, + dynamoDBClient + ); + + // token-generation-states + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + agreementState: itemState.active, + GSIPK_consumerId_eserviceId, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient, genericLogger); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + platformStatesAgreementEntryPK, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(platformStatesAgreementEntry); + + // token-generation-states + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + tokenGenStatesConsumerClient1, + tokenGenStatesConsumerClient2, + ]) + ); + }); + it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts index 25ef6d69ec..ea4aaee0fd 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV1.ts @@ -103,9 +103,7 @@ export async function handleMessageV1( existingCatalogEntry.version > msg.version ) { logger.info( - `Skipping processing of entry ${ - existingCatalogEntry?.PK - }. Reason: ${ + `Skipping processing of entry ${eserviceDescriptorPK}. Reason: ${ !existingCatalogEntry ? "entry not found in platform-states" : "a more recent entry already exists" diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index 1a057b7b85..e906263559 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -158,7 +158,7 @@ export async function handleMessageV2( if (!catalogEntry || catalogEntry.version > msg.version) { logger.info( - `Skipping processing of entry ${catalogEntry?.PK}. Reason: ${ + `Skipping processing of entry ${primaryKey}. Reason: ${ !catalogEntry ? "entry not found in platform-states" : "a more recent entry already exists" @@ -229,7 +229,7 @@ export async function handleMessageV2( if (!catalogEntry || catalogEntry.version > msg.version) { logger.info( - `Skipping processing of entry ${catalogEntry?.PK}. Reason: ${ + `Skipping processing of entry ${primaryKey}. Reason: ${ !catalogEntry ? "entry not found in platform-states" : "a more recent entry already exists" From a853f4e3cd94d33485e35c3bf14c90ae86169482 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:11:04 +0100 Subject: [PATCH 108/126] IMN-883 Add token-generation-readmodel-checker (#1119) Co-authored-by: Roberto Taglioni Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- package.json | 1 + .../token-generation-readmodel-checker/.env | 12 + .../Dockerfile | 42 + .../README.md | 3 + .../aws.config.local | 9 + .../package.json | 42 + .../src/configs/config.ts | 17 + .../src/index.ts | 25 + .../src/models/types.ts | 128 ++ .../src/services/readModelService.ts | 86 + .../tokenGenerationReadModelService.ts | 116 ++ .../src/utils/utils.ts | 866 ++++++++ .../tokenGenerationReadModelChecker.test.ts | 1836 +++++++++++++++++ .../test/tsconfig.json | 4 + .../test/utils.test.ts | 602 ++++++ .../test/utils.ts | 102 + .../test/vitestGlobalSetup.ts | 3 + .../tsconfig.check.json | 7 + .../tsconfig.json | 9 + .../vitest.config.ts | 11 + pnpm-lock.yaml | 81 +- 21 files changed, 3989 insertions(+), 13 deletions(-) create mode 100644 packages/token-generation-readmodel-checker/.env create mode 100644 packages/token-generation-readmodel-checker/Dockerfile create mode 100644 packages/token-generation-readmodel-checker/README.md create mode 100644 packages/token-generation-readmodel-checker/aws.config.local create mode 100644 packages/token-generation-readmodel-checker/package.json create mode 100644 packages/token-generation-readmodel-checker/src/configs/config.ts create mode 100644 packages/token-generation-readmodel-checker/src/index.ts create mode 100644 packages/token-generation-readmodel-checker/src/models/types.ts create mode 100644 packages/token-generation-readmodel-checker/src/services/readModelService.ts create mode 100644 packages/token-generation-readmodel-checker/src/services/tokenGenerationReadModelService.ts create mode 100644 packages/token-generation-readmodel-checker/src/utils/utils.ts create mode 100644 packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts create mode 100644 packages/token-generation-readmodel-checker/test/tsconfig.json create mode 100644 packages/token-generation-readmodel-checker/test/utils.test.ts create mode 100644 packages/token-generation-readmodel-checker/test/utils.ts create mode 100644 packages/token-generation-readmodel-checker/test/vitestGlobalSetup.ts create mode 100644 packages/token-generation-readmodel-checker/tsconfig.check.json create mode 100644 packages/token-generation-readmodel-checker/tsconfig.json create mode 100644 packages/token-generation-readmodel-checker/vitest.config.ts diff --git a/package.json b/package.json index b75e42c2b3..73b674c8c6 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "start:client-purpose-updater": "turbo start --filter pagopa-interop-client-purpose-updater", "start:authorization-server": "turbo start --filter pagopa-interop-authorization-server", "start:token-details-persister": "turbo start --filter pagopa-interop-token-details-persister", + "start:token-generation-readmodel-checker": "turbo start --filter pagopa-interop-token-generation-readmodel-checker", "test": "turbo test", "build": "turbo build", "check": "turbo check", diff --git a/packages/token-generation-readmodel-checker/.env b/packages/token-generation-readmodel-checker/.env new file mode 100644 index 0000000000..c3088711f1 --- /dev/null +++ b/packages/token-generation-readmodel-checker/.env @@ -0,0 +1,12 @@ +LOG_LEVEL=info + +READMODEL_DB_HOST="localhost" +READMODEL_DB_NAME="readmodel" +READMODEL_DB_USERNAME="root" +READMODEL_DB_PASSWORD="example" +READMODEL_DB_PORT=27017 + +AWS_CONFIG_FILE=aws.config.local + +TOKEN_GENERATION_READMODEL_TABLE_NAME_PLATFORM="platform-states" +TOKEN_GENERATION_READMODEL_TABLE_NAME_TOKEN_GENERATION="token-generation-states" diff --git a/packages/token-generation-readmodel-checker/Dockerfile b/packages/token-generation-readmodel-checker/Dockerfile new file mode 100644 index 0000000000..e975c567b0 --- /dev/null +++ b/packages/token-generation-readmodel-checker/Dockerfile @@ -0,0 +1,42 @@ +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as build + +RUN corepack enable + +WORKDIR /app +COPY package.json /app/ +COPY pnpm-lock.yaml /app/ +COPY pnpm-workspace.yaml /app/ +COPY .npmrc /app/ + +COPY ./packages/token-generation-readmodel-checker/package.json /app/packages/token-generation-readmodel-checker/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/token-generation-readmodel-checker /app/packages/token-generation-readmodel-checker/ +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/token-generation-readmodel-checker/node_modules \ + package*.json packages/token-generation-readmodel-checker/package*.json \ + packages/commons/ \ + packages/models/ \ + packages/token-generation-readmodel-checker/dist && \ + find /out -exec touch -h --date=@0 {} \; + +FROM node:20.14.0-slim@sha256:5e8ac65a0231d76a388683d07ca36a9769ab019a85d85169fe28e206f7a3208e as final + +COPY --from=build /out /app + +WORKDIR /app/packages/token-generation-readmodel-checker +EXPOSE 3000 + +CMD [ "node", "." ] diff --git a/packages/token-generation-readmodel-checker/README.md b/packages/token-generation-readmodel-checker/README.md new file mode 100644 index 0000000000..b45f8c7471 --- /dev/null +++ b/packages/token-generation-readmodel-checker/README.md @@ -0,0 +1,3 @@ +# Token generation read model comparison + +This job compares the Token Generation Read Model and the Read Model contents to check if the data is correct diff --git a/packages/token-generation-readmodel-checker/aws.config.local b/packages/token-generation-readmodel-checker/aws.config.local new file mode 100644 index 0000000000..f3016f81d6 --- /dev/null +++ b/packages/token-generation-readmodel-checker/aws.config.local @@ -0,0 +1,9 @@ +[default] +aws_access_key_id=key +aws_secret_access_key=secret +region=eu-south-1 +services=local + +[services local] +dynamodb= + endpoint_url=http://localhost:8085 diff --git a/packages/token-generation-readmodel-checker/package.json b/packages/token-generation-readmodel-checker/package.json new file mode 100644 index 0000000000..a3bdd24652 --- /dev/null +++ b/packages/token-generation-readmodel-checker/package.json @@ -0,0 +1,42 @@ +{ + "name": "pagopa-interop-token-generation-readmodel-checker", + "private": true, + "version": "1.0.0", + "description": "PagoPA Interoperability token-generation-readmodel-checker job", + "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": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "build": "tsc", + "check": "tsc --project tsconfig.check.json" + }, + "keywords": [], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@types/json-diff": "1.0.3", + "@pagopa/eslint-config": "3.0.0", + "@types/node": "20.14.6", + "pagopa-interop-commons-test": "workspace:*", + "prettier": "2.8.8", + "testcontainers": "10.9.0", + "tsx": "4.19.1", + "typescript": "5.4.5", + "vitest": "1.6.0" + }, + "dependencies": { + "@aws-sdk/client-dynamodb": "3.693.0", + "@aws-sdk/util-dynamodb": "3.693.0", + "dotenv-flow": "4.1.0", + "json-diff": "1.0.6", + "pagopa-interop-commons": "workspace:*", + "pagopa-interop-models": "workspace:*", + "zod": "3.23.8", + "ts-pattern": "5.2.0" + } +} diff --git a/packages/token-generation-readmodel-checker/src/configs/config.ts b/packages/token-generation-readmodel-checker/src/configs/config.ts new file mode 100644 index 0000000000..d38a480510 --- /dev/null +++ b/packages/token-generation-readmodel-checker/src/configs/config.ts @@ -0,0 +1,17 @@ +import { + LoggerConfig, + ReadModelDbConfig, + TokenGenerationReadModelDbConfig, +} from "pagopa-interop-commons"; +import { z } from "zod"; + +const TokenReadModelCheckerConfig = LoggerConfig.and(ReadModelDbConfig).and( + TokenGenerationReadModelDbConfig +); + +export type TokenReadModelCheckerConfig = z.infer< + typeof TokenReadModelCheckerConfig +>; + +export const config: TokenReadModelCheckerConfig = + TokenReadModelCheckerConfig.parse(process.env); diff --git a/packages/token-generation-readmodel-checker/src/index.ts b/packages/token-generation-readmodel-checker/src/index.ts new file mode 100644 index 0000000000..d6f6816ca8 --- /dev/null +++ b/packages/token-generation-readmodel-checker/src/index.ts @@ -0,0 +1,25 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { logger } from "pagopa-interop-commons"; +import { CorrelationId, generateId } from "pagopa-interop-models"; +import { compareTokenGenerationReadModel } from "./utils/utils.js"; + +const dynamoDBClient = new DynamoDBClient(); +const loggerInstance = logger({ + serviceName: "token-generation-readmodel-checker", + correlationId: generateId(), +}); + +async function main(): Promise { + const differencesCount = await compareTokenGenerationReadModel( + dynamoDBClient, + loggerInstance + ); + + if (differencesCount > 0) { + loggerInstance.error(`Differences count: ${differencesCount}`); + } else { + loggerInstance.info("No differences found"); + } +} + +await main(); diff --git a/packages/token-generation-readmodel-checker/src/models/types.ts b/packages/token-generation-readmodel-checker/src/models/types.ts new file mode 100644 index 0000000000..31f956f39b --- /dev/null +++ b/packages/token-generation-readmodel-checker/src/models/types.ts @@ -0,0 +1,128 @@ +import { + Agreement, + Client, + clientKindTokenGenStates, + EService, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + TokenGenerationStatesApiClient, + TokenGenerationStatesConsumerClient, +} from "pagopa-interop-models"; +import { z } from "zod"; + +// purpose +export const ComparisonPurpose = Purpose.pick({ + id: true, + consumerId: true, + eserviceId: true, + versions: true, +}); +export type ComparisonPurpose = z.infer; + +export const ComparisonPlatformStatesPurposeEntry = + PlatformStatesPurposeEntry.pick({ + PK: true, + state: true, + purposeConsumerId: true, + purposeEserviceId: true, + purposeVersionId: true, + }); +export type ComparisonPlatformStatesPurposeEntry = z.infer< + typeof ComparisonPlatformStatesPurposeEntry +>; + +// agreement +export const ComparisonAgreement = Agreement.pick({ + id: true, + state: true, + consumerId: true, + eserviceId: true, + descriptorId: true, +}); +export type ComparisonAgreement = z.infer; + +export const ComparisonPlatformStatesAgreementEntry = + PlatformStatesAgreementEntry.pick({ + PK: true, + state: true, + GSIPK_consumerId_eserviceId: true, + agreementDescriptorId: true, + GSISK_agreementTimestamp: true, + }); +export type ComparisonPlatformStatesAgreementEntry = z.infer< + typeof ComparisonPlatformStatesAgreementEntry +>; + +// catalog +export const ComparisonEService = EService.pick({ + id: true, + descriptors: true, +}); +export type ComparisonEService = z.infer; + +export const ComparisonPlatformStatesCatalogEntry = + PlatformStatesCatalogEntry.pick({ + PK: true, + state: true, + descriptorAudience: true, + descriptorVoucherLifespan: true, + }); +export type ComparisonPlatformStatesCatalogEntry = z.infer< + typeof ComparisonPlatformStatesCatalogEntry +>; + +// client +export const ComparisonClient = Client.pick({ + id: true, + kind: true, + consumerId: true, + purposes: true, +}); +export type ComparisonClient = z.infer; + +export const ComparisonPlatformStatesClientEntry = + PlatformStatesClientEntry.pick({ + PK: true, + clientKind: true, + clientConsumerId: true, + clientPurposesIds: true, + }); +export type ComparisonPlatformStatesClientEntry = z.infer< + typeof ComparisonPlatformStatesClientEntry +>; + +export const ComparisonTokenGenStatesGenericClient = + TokenGenerationStatesConsumerClient.pick({ + consumerId: true, + GSIPK_clientId: true, + GSIPK_clientId_kid: true, + publicKey: true, + GSIPK_clientId_purposeId: true, + GSIPK_purposeId: true, + purposeState: true, + purposeVersionId: true, + agreementId: true, + agreementState: true, + GSIPK_consumerId_eserviceId: true, + GSIPK_eserviceId_descriptorId: true, + descriptorState: true, + descriptorAudience: true, + descriptorVoucherLifespan: true, + }) + .extend({ + PK: + TokenGenerationStatesConsumerClient.shape.PK || + TokenGenerationStatesApiClient.shape.PK, + clientKind: z.union([ + z.literal(clientKindTokenGenStates.consumer), + z.literal(clientKindTokenGenStates.api), + ]), + }) + .partial(); + +export type ComparisonTokenGenStatesGenericClient = z.infer< + typeof ComparisonTokenGenStatesGenericClient +>; diff --git a/packages/token-generation-readmodel-checker/src/services/readModelService.ts b/packages/token-generation-readmodel-checker/src/services/readModelService.ts new file mode 100644 index 0000000000..edb98e3f60 --- /dev/null +++ b/packages/token-generation-readmodel-checker/src/services/readModelService.ts @@ -0,0 +1,86 @@ +import { ReadModelRepository } from "pagopa-interop-commons"; +import { + Agreement, + Client, + EService, + genericInternalError, + Purpose, +} from "pagopa-interop-models"; +import { z } from "zod"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function readModelServiceBuilder(readModel: ReadModelRepository) { + return { + async getAllReadModelPurposes(): Promise { + const data = await readModel.purposes.find().toArray(); + + if (!data) { + return []; + } else { + const results = z.array(Purpose).safeParse(data.map((d) => d.data)); + if (!results.success) { + throw genericInternalError( + `Unable to parse purpose items: results ${JSON.stringify( + results + )} - data ${JSON.stringify(data)} ` + ); + } + return results.data; + } + }, + + async getAllReadModelAgreements(): Promise { + const data = await readModel.agreements.find().toArray(); + + if (!data) { + return []; + } else { + const results = z.array(Agreement).safeParse(data.map((d) => d.data)); + if (!results.success) { + throw genericInternalError( + `Unable to parse agreement items: results ${JSON.stringify( + results + )} - data ${JSON.stringify(data)} ` + ); + } + return results.data; + } + }, + + async getAllReadModelClients(): Promise { + const data = await readModel.clients.find().toArray(); + + if (!data) { + return []; + } else { + const results = z.array(Client).safeParse(data.map((d) => d.data)); + if (!results.success) { + throw genericInternalError( + `Unable to parse client items: results ${JSON.stringify( + results + )} - data ${JSON.stringify(data)} ` + ); + } + return results.data; + } + }, + + async getAllReadModelEServices(): Promise { + const data = await readModel.eservices.find().toArray(); + + if (!data) { + return []; + } else { + const results = z.array(EService).safeParse(data.map((d) => d.data)); + if (!results.success) { + throw genericInternalError( + `Unable to parse eservice items: results ${JSON.stringify( + results + )} - data ${JSON.stringify(data)} ` + ); + } + return results.data; + } + }, + }; +} diff --git a/packages/token-generation-readmodel-checker/src/services/tokenGenerationReadModelService.ts b/packages/token-generation-readmodel-checker/src/services/tokenGenerationReadModelService.ts new file mode 100644 index 0000000000..b7e99aa379 --- /dev/null +++ b/packages/token-generation-readmodel-checker/src/services/tokenGenerationReadModelService.ts @@ -0,0 +1,116 @@ +import { + genericInternalError, + TokenGenerationStatesGenericClient, +} from "pagopa-interop-models"; +import { + AttributeValue, + DynamoDBClient, + ScanCommand, + ScanCommandOutput, + ScanInput, +} from "@aws-sdk/client-dynamodb"; +import { PlatformStatesGenericEntry } from "pagopa-interop-models"; +import { unmarshall } from "@aws-sdk/util-dynamodb"; +import { z } from "zod"; +import { config } from "../configs/config.js"; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function tokenGenerationReadModelServiceBuilder( + dynamoDBClient: DynamoDBClient +) { + return { + async readAllPlatformStatesItems(): Promise { + const runPaginatedQuery = async ( + exclusiveStartKey?: Record + ): Promise => { + const readInput: ScanInput = { + TableName: config.tokenGenerationReadModelTableNamePlatform, + ExclusiveStartKey: exclusiveStartKey, + }; + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); + + if (!data.Items) { + throw genericInternalError( + `Unable to read platform-states entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const platformStateEntries = z + .array(PlatformStatesGenericEntry) + .safeParse(unmarshalledItems); + + if (!platformStateEntries.success) { + throw genericInternalError( + `Unable to parse platform-states entries: result ${JSON.stringify( + platformStateEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return platformStateEntries.data; + } else { + return [ + ...platformStateEntries.data, + ...(await runPaginatedQuery(data.LastEvaluatedKey)), + ]; + } + } + }; + + return await runPaginatedQuery(); + }, + + async readAllTokenGenerationStatesItems(): Promise< + TokenGenerationStatesGenericClient[] + > { + const runPaginatedQuery = async ( + exclusiveStartKey?: Record + ): Promise => { + const readInput: ScanInput = { + TableName: config.tokenGenerationReadModelTableNameTokenGeneration, + ExclusiveStartKey: exclusiveStartKey, + }; + const commandQuery = new ScanCommand(readInput); + const data: ScanCommandOutput = await dynamoDBClient.send(commandQuery); + + if (!data.Items) { + throw genericInternalError( + `Unable to read token-generation-states entries: result ${JSON.stringify( + data + )} ` + ); + } else { + const unmarshalledItems = data.Items.map((item) => unmarshall(item)); + + const tokenGenStatesEntries = z + .array(TokenGenerationStatesGenericClient) + .safeParse(unmarshalledItems); + + if (!tokenGenStatesEntries.success) { + throw genericInternalError( + `Unable to parse token-generation-states entries: result ${JSON.stringify( + tokenGenStatesEntries + )} - data ${JSON.stringify(data)} ` + ); + } + + if (!data.LastEvaluatedKey) { + return tokenGenStatesEntries.data; + } else { + return [ + ...tokenGenStatesEntries.data, + ...(await runPaginatedQuery(data.LastEvaluatedKey)), + ]; + } + } + }; + + return await runPaginatedQuery(); + }, + }; +} diff --git a/packages/token-generation-readmodel-checker/src/utils/utils.ts b/packages/token-generation-readmodel-checker/src/utils/utils.ts new file mode 100644 index 0000000000..bb9578fbe8 --- /dev/null +++ b/packages/token-generation-readmodel-checker/src/utils/utils.ts @@ -0,0 +1,866 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { Logger, ReadModelRepository } from "pagopa-interop-commons"; +import { + Agreement, + AgreementId, + agreementState, + AgreementState, + Client, + ClientId, + clientKind, + ClientKind, + ClientKindTokenGenStates, + clientKindTokenGenStates, + Descriptor, + DescriptorId, + DescriptorState, + descriptorState, + EService, + EServiceId, + genericInternalError, + GSIPKConsumerIdEServiceId, + itemState, + ItemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKClientIdKid, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesAgreementPK, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesClientPK, + PlatformStatesEServiceDescriptorPK, + PlatformStatesPurposeEntry, + PlatformStatesPurposePK, + Purpose, + PurposeId, + PurposeVersion, + purposeVersionState, + TokenGenerationStatesClientKidPK, + TokenGenerationStatesClientKidPurposePK, + TokenGenerationStatesGenericClient, + unsafeBrandId, +} from "pagopa-interop-models"; +import { match } from "ts-pattern"; +import { diff } from "json-diff"; +import { config } from "../configs/config.js"; +import { + ComparisonPlatformStatesAgreementEntry, + ComparisonPlatformStatesCatalogEntry, + ComparisonPlatformStatesPurposeEntry, + ComparisonTokenGenStatesGenericClient, +} from "../models/types.js"; +import { readModelServiceBuilder } from "../services/readModelService.js"; +import { tokenGenerationReadModelServiceBuilder } from "../services/tokenGenerationReadModelService.js"; + +export function getLastPurposeVersion( + purposeVersions: PurposeVersion[] +): PurposeVersion | undefined { + return purposeVersions + .filter( + (pv) => + pv.state === purposeVersionState.active || + pv.state === purposeVersionState.suspended || + pv.state === purposeVersionState.archived + ) + .toSorted( + (purposeVersion1, purposeVersion2) => + purposeVersion2.createdAt.getTime() - + purposeVersion1.createdAt.getTime() + )[0]; +} + +export function getPurposeStateFromPurposeVersion( + purposeVersion: PurposeVersion +): ItemState { + if (purposeVersion.state === purposeVersionState.active) { + return itemState.active; + } else { + return itemState.inactive; + } +} + +export function getLastAgreement(agreements: Agreement[]): Agreement { + return agreements + .filter( + (a) => + a.state === agreementState.active || + a.state === agreementState.suspended || + a.state === agreementState.archived + ) + .toSorted( + (agreement1, agreement2) => + agreement2.createdAt.getTime() - agreement1.createdAt.getTime() + )[0]; +} + +export function getValidDescriptors(descriptors: Descriptor[]): Descriptor[] { + return descriptors.filter( + (descriptor) => + descriptor.state === descriptorState.published || + descriptor.state === descriptorState.suspended || + descriptor.state === descriptorState.deprecated + ); +} + +function getIdFromPlatformStatesPK< + T extends PurposeId | AgreementId | ClientId +>( + pk: + | PlatformStatesPurposePK + | PlatformStatesAgreementPK + | PlatformStatesClientPK +): T { + return unsafeBrandId(pk.split("#")[1]); +} + +function getCatalogIdsFromPlatformStatesPK( + pk: PlatformStatesEServiceDescriptorPK +): { + eserviceId: EServiceId; + descriptorId: DescriptorId; +} { + const splitPK = pk.split("#"); + return { + eserviceId: unsafeBrandId(splitPK[1]), + descriptorId: unsafeBrandId(splitPK[2]), + }; +} + +function getClientIdFromTokenGenStatesPK( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): ClientId { + const splitPK = pk.split("#"); + return unsafeBrandId(splitPK[1]); +} + +function getKidFromTokenGenStatesPK( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): string { + const splitPK = pk.split("#"); + return unsafeBrandId(splitPK[2]); +} + +function getPurposeIdFromTokenGenStatesPK( + pk: TokenGenerationStatesClientKidPurposePK | TokenGenerationStatesClientKidPK +): PurposeId | undefined { + const splitPK = pk.split("#"); + return unsafeBrandId(splitPK[3]); +} + +export async function compareTokenGenerationReadModel( + dynamoDBClient: DynamoDBClient, + logger: Logger +): Promise { + logger.info( + "Token generation read model and read model comparison started.\n" + ); + logger.info("> Connecting to database..."); + const readModel = ReadModelRepository.init(config); + const readModelService = readModelServiceBuilder(readModel); + logger.info("> Connected to database!\n"); + + const tokenGenerationService = + tokenGenerationReadModelServiceBuilder(dynamoDBClient); + const platformStatesEntries = + await tokenGenerationService.readAllPlatformStatesItems(); + const tokenGenerationStatesEntries = + await tokenGenerationService.readAllTokenGenerationStatesItems(); + + const tokenGenStatesByClient = tokenGenerationStatesEntries.reduce( + (acc: Map, entry) => { + const clientId = getClientIdFromTokenGenStatesPK(entry.PK); + acc.set(clientId, [...(acc.get(clientId) || []), entry]); + return acc; + }, + new Map() + ); + + const platformStates: { + purposes: Map; + agreements: Map; + eservices: Map>; + } = platformStatesEntries.reduce<{ + purposes: Map; + agreements: Map; + eservices: Map>; + }>( + (acc, e) => { + const parsedPurpose = PlatformStatesPurposeEntry.safeParse(e); + if (parsedPurpose.success) { + acc.purposes.set( + unsafeBrandId( + getIdFromPlatformStatesPK(parsedPurpose.data.PK) + ), + parsedPurpose.data + ); + return acc; + } + + const parsedAgreement = PlatformStatesAgreementEntry.safeParse(e); + if (parsedAgreement.success) { + acc.agreements.set( + unsafeBrandId( + getIdFromPlatformStatesPK(parsedAgreement.data.PK) + ), + parsedAgreement.data + ); + return acc; + } + + const parsedCatalog = PlatformStatesCatalogEntry.safeParse(e); + if (parsedCatalog.success) { + const catalogIds = getCatalogIdsFromPlatformStatesPK( + parsedCatalog.data.PK + ); + + acc.eservices.set( + catalogIds.eserviceId, + ( + acc.eservices.get(catalogIds.eserviceId) ?? + new Map() + ).set(catalogIds.descriptorId, parsedCatalog.data) + ); + + return acc; + } + + if (PlatformStatesClientEntry.safeParse(e).success) { + return acc; + } + + throw genericInternalError( + `Unknown platform-states type for entry: ${JSON.stringify(e)} ` + ); + }, + { + purposes: new Map(), + agreements: new Map(), + eservices: new Map< + EServiceId, + Map + >(), + } + ); + + const purposes = await readModelService.getAllReadModelPurposes(); + const purposesById = new Map( + purposes.map((purpose) => [purpose.id, purpose]) + ); + + const agreements = await readModelService.getAllReadModelAgreements(); + const agreementsById = new Map(); + const agreementsByConsumerIdEserviceId = new Map< + GSIPKConsumerIdEServiceId, + Agreement[] + >(); + + for (const agreement of agreements) { + agreementsById.set(agreement.id, agreement); + + const consumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const existingAgreements = + agreementsByConsumerIdEserviceId.get(consumerIdEServiceId); + + agreementsByConsumerIdEserviceId.set(consumerIdEServiceId, [ + ...(existingAgreements || []), + agreement, + ]); + } + + const eservices = await readModelService.getAllReadModelEServices(); + const eservicesById = new Map(); + for (const eservice of eservices) { + eservicesById.set(eservice.id, eservice); + } + + const clients = await readModelService.getAllReadModelClients(); + const clientsById = new Map( + clients.map((client) => [unsafeBrandId(client.id), client]) + ); + + const purposeDifferences = await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: platformStates.purposes, + purposesById, + logger, + }); + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: platformStates.agreements, + agreementsById, + logger, + }); + const catalogDifferences = await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: platformStates.eservices, + eservicesById, + logger, + }); + const clientAndTokenGenStatesDifferences = + await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient, + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger, + }); + + return ( + purposeDifferences + + agreementDifferences + + catalogDifferences + + clientAndTokenGenStatesDifferences + ); +} + +// purposes +// eslint-disable-next-line sonarjs/cognitive-complexity +export async function compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById, + purposesById, + logger, +}: { + platformStatesPurposeById: Map; + purposesById: Map; + logger: Logger; +}): Promise { + const allIds = new Set([ + ...platformStatesPurposeById.keys(), + ...purposesById.keys(), + ]); + + // eslint-disable-next-line functional/no-let + let differencesCount = 0; + for (const id of allIds) { + const platformStatesEntry = platformStatesPurposeById.get(id); + const purpose = purposesById.get(id); + + if (!platformStatesEntry && !purpose) { + throw genericInternalError( + `Purpose and platform-states entry not found for id: ${id}` + ); + } + + if (platformStatesEntry && !purpose) { + logger.error(`Read model purpose not found for id: ${id}`); + differencesCount++; + } + + if (purpose) { + const lastPurposeVersion = getLastPurposeVersion(purpose.versions); + + if (!platformStatesEntry) { + const hasValidState = + lastPurposeVersion && + lastPurposeVersion.state !== purposeVersionState.archived; + if (hasValidState) { + logger.error( + `Purpose platform-states entry is missing for purpose with id: ${purpose.id}` + ); + differencesCount++; + } + } + + if (platformStatesEntry && lastPurposeVersion) { + const expectedPlatformStatesPurposeEntry: ComparisonPlatformStatesPurposeEntry = + { + PK: makePlatformStatesPurposePK(purpose.id), + state: getPurposeStateFromPurposeVersion(lastPurposeVersion), + purposeVersionId: lastPurposeVersion.id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + }; + + const objectsDiff = diff( + ComparisonPlatformStatesPurposeEntry.parse(platformStatesEntry), + expectedPlatformStatesPurposeEntry, + { sort: true } + ); + if (objectsDiff) { + differencesCount++; + // For info: __old = platform-states entry and __new = read model purpose + logger.error( + `Differences in platform-states when checking purpose with id ${purpose.id}` + ); + logger.error(JSON.stringify(objectsDiff, null, 2)); + } + } + } + } + + return differencesCount; +} + +// agreements +export async function compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById, + agreementsById, + logger, +}: { + platformStatesAgreementById: Map; + agreementsById: Map; + logger: Logger; +}): Promise { + const allIds = new Set([ + ...platformStatesAgreementById.keys(), + ...agreementsById.keys(), + ]); + + // eslint-disable-next-line functional/no-let + let differencesCount = 0; + for (const id of allIds) { + const platformStatesEntry = platformStatesAgreementById.get(id); + const agreement = agreementsById.get(id); + + if (!platformStatesEntry && !agreement) { + throw genericInternalError( + `Agreement and platform-states entry not found for id: ${id}` + ); + } + + if (platformStatesEntry && !agreement) { + logger.error(`Read model agreement not found for id: ${id}`); + differencesCount++; + } + + if ( + !platformStatesEntry && + agreement && + (agreement.state === agreementState.active || + agreement.state === agreementState.suspended) + ) { + logger.error(`platform-states agreement not found for id: ${id}`); + differencesCount++; + } + + if (platformStatesEntry && agreement) { + const expectedPlatformStatesAgreementEntry: ComparisonPlatformStatesAgreementEntry = + { + PK: makePlatformStatesAgreementPK(agreement.id), + state: agreementStateToItemState(agreement.state), + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + agreement.stamps.activation?.when.toISOString() || + agreement.createdAt.toISOString(), + agreementDescriptorId: agreement.descriptorId, + }; + + const objectsDiff = diff( + ComparisonPlatformStatesAgreementEntry.parse(platformStatesEntry), + expectedPlatformStatesAgreementEntry, + { sort: true, excludeKeys: ["GSISK_agreementTimestamp"] } + ); + if (objectsDiff) { + differencesCount++; + // For info: __old = platform-states agreement and __new = read model agreement + logger.error( + `Differences in platform-states when checking agreement with id ${agreement.id}` + ); + logger.error(JSON.stringify(objectsDiff, null, 2)); + } + } + } + return differencesCount; +} + +// eservices +// eslint-disable-next-line sonarjs/cognitive-complexity +export async function compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById, + eservicesById, + logger, +}: { + platformStatesEServiceById: Map< + EServiceId, + Map + >; + eservicesById: Map; + logger: Logger; +}): Promise { + const allIds = new Set([ + ...platformStatesEServiceById.keys(), + ...eservicesById.keys(), + ]); + + // eslint-disable-next-line functional/no-let + let differencesCount = 0; + for (const id of allIds) { + const eservice = eservicesById.get(id); + + if (!eservice) { + differencesCount++; + logger.error(`Read model e-service not found for id: ${id}`); + } else { + // Descriptors with a state other than deprecated, published or suspended are not considered because they are not expected in the platform-states + if ( + !eservice.descriptors.some( + (d) => + d.state === descriptorState.deprecated || + d.state === descriptorState.published || + d.state === descriptorState.suspended + ) + ) { + continue; + } + + const platformStatesEntries = platformStatesEServiceById.get(id); + + if (!platformStatesEntries) { + logger.error( + `platform-states entries not found for e-service with id: ${id}` + ); + differencesCount++; + continue; + } + + const expectedDescriptorsMap = new Map< + DescriptorId, + ComparisonPlatformStatesCatalogEntry + >(); + + eservice.descriptors.forEach((descriptor) => { + expectedDescriptorsMap.set(descriptor.id, { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }), + state: descriptorStateToItemState(descriptor.state), + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + }); + }); + const allDescriptorIds = new Set([ + ...expectedDescriptorsMap.keys(), + ...platformStatesEntries.keys(), + ]); + + for (const descriptorId of allDescriptorIds) { + const readModelEntry = expectedDescriptorsMap.get(descriptorId); + const platformStatesEntry = platformStatesEntries.get(descriptorId); + + if (platformStatesEntry && !readModelEntry) { + logger.error(`Read model e-service not found for id: ${id}`); + differencesCount++; + } + + if (platformStatesEntry && readModelEntry) { + const objectsDiff = diff( + ComparisonPlatformStatesCatalogEntry.parse(platformStatesEntry), + readModelEntry, + { sort: true } + ); + if (objectsDiff) { + differencesCount++; + // For info: __old = platform-states entry and __new = read model e-service + logger.error( + `Differences in platform-states when checking e-service with id ${eservice.id}` + ); + logger.error(JSON.stringify(objectsDiff, null, 2)); + } + } + } + } + } + return differencesCount; +} + +// clients +export async function compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient, + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger, +}: { + tokenGenStatesByClient: Map; + clientsById: Map; + purposesById: Map; + eservicesById: Map; + agreementsByConsumerIdEserviceId: Map; + logger: Logger; +}): Promise { + const allIds = new Set([ + ...tokenGenStatesByClient.keys(), + ...clientsById.keys(), + ]); + + // eslint-disable-next-line functional/no-let + let differencesCount = 0; + + for (const id of allIds) { + const tokenGenStatesEntries = tokenGenStatesByClient.get(id); + const client = clientsById.get(id); + + if (client) { + differencesCount += validateTokenGenerationStates({ + tokenGenStatesEntries, + client, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger, + }); + } else { + logger.error(`Read model client not found for id: ${id}`); + differencesCount++; + } + } + + return differencesCount; +} + +// eslint-disable-next-line complexity, sonarjs/cognitive-complexity +function validateTokenGenerationStates({ + tokenGenStatesEntries, + client, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger, +}: { + tokenGenStatesEntries: TokenGenerationStatesGenericClient[] | undefined; + client: Client; + purposesById: Map; + eservicesById: Map; + agreementsByConsumerIdEserviceId: Map; + logger: Logger; +}): number { + const expectedTokenGenStatesEntriesCount = + client.purposes.length > 0 + ? client.keys.length * client.purposes.length + : client.keys.length; + + if (!tokenGenStatesEntries || tokenGenStatesEntries.length === 0) { + if (client.keys.length === 0) { + return 0; + } + + logger.error( + `Client ${client.id} has ${client.keys.length} ${ + client.keys.length > 1 ? "keys" : "key" + } but zero token-generation-states entries` + ); + return expectedTokenGenStatesEntriesCount; + } + + // eslint-disable-next-line functional/no-let + let differencesCount = 0; + for (const e of tokenGenStatesEntries) { + const extractedKid = getKidFromTokenGenStatesPK(e.PK); + const key = client.keys.find( + (k) => k.encodedPem === e.publicKey && k.kid === extractedKid + ); + + function compareClientKidEntry(): void { + if ( + !TokenGenerationStatesClientKidPK.safeParse(e.PK).success && + e.clientKind === clientKindTokenGenStates.consumer + ) { + logger.error( + `token-generation-states entry has PK ${e.PK}, but should have a CLIENTKID PK` + ); + } + + const comparisonTokenGenStatesEntry: ComparisonTokenGenStatesGenericClient = + { + PK: key + ? makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }) + : undefined, + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + publicKey: key?.encodedPem, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: key + ? makeGSIPKClientIdKid({ clientId: client.id, kid: key.kid }) + : undefined, + }; + + const objectsDiff = diff( + ComparisonTokenGenStatesGenericClient.parse(e), + comparisonTokenGenStatesEntry, + { sort: true } + ); + if (objectsDiff) { + differencesCount++; + logger.error( + `Differences in token-generation-states when checking entry with PK ${e.PK}` + ); + logger.error(JSON.stringify(objectsDiff, null, 2)); + } + } + + match(e) + .with({ clientKind: clientKindTokenGenStates.consumer }, (e) => { + if (client.purposes.length !== 0) { + // TokenGenerationStatesConsumerClient with CLIENTKIDPURPOSE PK + const purposeId = getPurposeIdFromTokenGenStatesPK(e.PK); + const purpose = purposeId ? purposesById.get(purposeId) : undefined; + if (!purpose) { + if ( + TokenGenerationStatesClientKidPurposePK.safeParse(e.PK).success + ) { + if (!e.purposeState || e.purposeState === itemState.inactive) { + return; + } + + logger.error( + `no purpose found in read model for token-generation-states entry with PK ${e.PK}` + ); + } else { + logger.error( + `token-generation-states entry has PK ${e.PK}, but should have a CLIENTKIDPURPOSE PK` + ); + } + + differencesCount++; + return; + } + + const lastPurposeVersion = getLastPurposeVersion(purpose.versions); + const agreements = agreementsByConsumerIdEserviceId.get( + makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purpose.eserviceId, + }) + ); + + if (!agreements) { + logger.error( + `no agreements found in read model for token-generation-states entry with PK ${e.PK}` + ); + + differencesCount++; + return; + } + + const agreement = getLastAgreement(agreements); + const agreementItemState = agreementStateToItemState(agreement.state); + + const eservice = eservicesById.get(agreement.eserviceId); + + const descriptor = eservice?.descriptors.find( + (d) => d.id === agreement.descriptorId + ); + + if (!eservice || !descriptor) { + const missingEServiceDescriptor = [ + !eservice ? "e-service" : null, + !descriptor ? "descriptor" : null, + ] + .filter(Boolean) + .join(" and "); + + logger.error( + `no ${missingEServiceDescriptor} in read model for token-generation-states entry with PK ${e.PK}` + ); + + differencesCount++; + return; + } + + const comparisonTokenGenStatesEntry: ComparisonTokenGenStatesGenericClient = + { + PK: key + ? makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: key.kid, + purposeId: purpose.id, + }) + : undefined, + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind( + client.kind + ), + publicKey: key?.encodedPem, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: key + ? makeGSIPKClientIdKid({ clientId: client.id, kid: key.kid }) + : undefined, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + + ...(lastPurposeVersion + ? { + purposeState: + getPurposeStateFromPurposeVersion(lastPurposeVersion), + purposeVersionId: lastPurposeVersion.id, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: client.consumerId, + eserviceId: purpose.eserviceId, + }), + agreementId: agreement.id, + agreementState: agreementItemState, + GSIPK_eserviceId_descriptorId: + makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }), + descriptorState: descriptorStateToItemState( + descriptor.state + ), + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + } + : {}), + }; + + const objectsDiff = diff( + ComparisonTokenGenStatesGenericClient.parse(e), + comparisonTokenGenStatesEntry, + { sort: true } + ); + if (objectsDiff) { + differencesCount++; + logger.error( + `Differences in token-generation-states when checking entry with PK ${e.PK}` + ); + logger.error(JSON.stringify(objectsDiff, null, 2)); + } + } else { + // TokenGenerationStatesConsumerClient with CLIENTKID PK + compareClientKidEntry(); + } + }) + .with({ clientKind: clientKindTokenGenStates.api }, () => { + compareClientKidEntry(); + }) + .exhaustive(); + } + + return differencesCount; +} + +export const agreementStateToItemState = (state: AgreementState): ItemState => + state === agreementState.active ? itemState.active : itemState.inactive; + +export const clientKindToTokenGenerationStatesClientKind = ( + kind: ClientKind +): ClientKindTokenGenStates => + match(kind) + .with(clientKind.consumer, () => clientKindTokenGenStates.consumer) + .with(clientKind.api, () => clientKindTokenGenStates.api) + .exhaustive(); + +export const descriptorStateToItemState = (state: DescriptorState): ItemState => + state === descriptorState.published || state === descriptorState.deprecated + ? itemState.active + : itemState.inactive; diff --git a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts new file mode 100644 index 0000000000..660c7bea37 --- /dev/null +++ b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts @@ -0,0 +1,1836 @@ +import { genericLogger } from "pagopa-interop-commons"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockAgreement, + getMockClient, + getMockDescriptor, + getMockEService, + getMockKey, + getMockPurpose, + getMockPurposeVersion, + getMockTokenGenStatesConsumerClient, + writePlatformAgreementEntry, + writePlatformCatalogEntry, + writePlatformPurposeEntry, + writeTokenGenStatesConsumerClient, +} from "pagopa-interop-commons-test"; +import { + Agreement, + agreementState, + Client, + clientKindTokenGenStates, + Descriptor, + descriptorState, + EService, + generateId, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKEServiceIdDescriptorId, + makeGSIPKClientIdKid, + makePlatformStatesAgreementPK, + makePlatformStatesClientPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesClientEntry, + PlatformStatesPurposeEntry, + Purpose, + purposeVersionState, + TokenGenerationStatesConsumerClient, +} from "pagopa-interop-models"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; +import { + clientKindToTokenGenerationStatesClientKind, + compareReadModelAgreementsWithPlatformStates, + compareReadModelClientsAndTokenGenStates, + compareReadModelEServicesWithPlatformStates, + compareReadModelPurposesWithPlatformStates, + compareTokenGenerationReadModel, +} from "../src/utils/utils.js"; +import { + addOneAgreement, + addOneClient, + addOneEService, + addOnePurpose, + dynamoDBClient, + writePlatformStatesClientEntry, +} from "./utils.js"; + +describe("Token Generation Read Model Checker tests", () => { + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + const mockDate = new Date(); + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(mockDate); + }); + afterAll(() => { + vi.useRealTimers(); + }); + + describe("all collections", () => { + it("should detect differences for all collections when the Token Generation Read Model states are wrong", async () => { + // catalog + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + await addOneEService(eservice); + + // purpose + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + await addOnePurpose(purpose); + + // agreement + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement); + + // client + const client: Client = { + ...getMockClient(), + purposes: [purpose.id, generateId()], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + await addOneClient(client); + + // platform-states + const purposeEntryPK = makePlatformStatesPurposePK(purpose.id); + const platformStatesPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPK, + state: itemState.active, + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: generateId(), + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry, + dynamoDBClient + ); + + const agreementEntryPK = makePlatformStatesAgreementPK(agreement.id); + const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPK, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: generateId(), + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry, + dynamoDBClient + ); + + const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }); + const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { + PK: catalogEntryPK, + state: itemState.active, + descriptorAudience: ["wrong-audience"], + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry, + dynamoDBClient + ); + + const clientEntryPK = makePlatformStatesClientPK(client.id); + const platformStatesClientEntry: PlatformStatesClientEntry = { + PK: clientEntryPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: [], + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformStatesClientEntry( + platformStatesClientEntry, + dynamoDBClient + ); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: `kid ${Math.random()}`, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient( + tokenGenStatesClientKidPurposePK + ), + GSIPK_purposeId: purpose.id, + purposeState: itemState.inactive, + purposeVersionId: purpose.versions[0].id, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + + consumerId: agreement.consumerId, + agreementId: agreement.id, + agreementState: itemState.inactive, + + descriptorState: itemState.inactive, + descriptorAudience: ["wrong-audience-2"], + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }), + + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: generateId(), + purposeId: generateId(), + }), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 4; + const differencesCount = await compareTokenGenerationReadModel( + dynamoDBClient, + genericLogger + ); + + expect(differencesCount).toEqual(expectedDifferencesLength); + }); + }); + + describe("purposes", () => { + it("should not detect differences when the purpose platform-states entries are correct", async () => { + const purpose1 = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + await addOnePurpose(purpose1); + + const purpose2 = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + await addOnePurpose(purpose2); + + // platform-states + const purposeEntryPK1 = makePlatformStatesPurposePK(purpose1.id); + const platformStatesPurposeEntry1: PlatformStatesPurposeEntry = { + PK: purposeEntryPK1, + state: itemState.active, + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry1, + dynamoDBClient + ); + + const purposeEntryPK2 = makePlatformStatesPurposePK(purpose2.id); + const platformStatesPurposeEntry2: PlatformStatesPurposeEntry = { + PK: purposeEntryPK2, + state: itemState.active, + purposeVersionId: purpose2.versions[0].id, + purposeEserviceId: purpose2.eserviceId, + purposeConsumerId: purpose2.consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry2, + dynamoDBClient + ); + + const expectedDifferencesLength = 0; + const purposeDifferences = + await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map([ + [purpose1.id, platformStatesPurposeEntry1], + [purpose2.id, platformStatesPurposeEntry2], + ]), + purposesById: new Map([ + [purpose1.id, purpose1], + [purpose2.id, purpose2], + ]), + logger: genericLogger, + }); + expect(purposeDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences for wrong purpose states", async () => { + const purpose1 = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + await addOnePurpose(purpose1); + + const purpose2 = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + await addOnePurpose(purpose2); + + // platform-states + const purposeEntryPK1 = makePlatformStatesPurposePK(purpose1.id); + const platformStatesPurposeEntry1: PlatformStatesPurposeEntry = { + PK: purposeEntryPK1, + state: itemState.inactive, + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry1, + dynamoDBClient + ); + + const purposeEntryPK2 = makePlatformStatesPurposePK(purpose2.id); + const platformStatesPurposeEntry2: PlatformStatesPurposeEntry = { + PK: purposeEntryPK2, + state: itemState.active, + purposeVersionId: generateId(), + purposeEserviceId: generateId(), + purposeConsumerId: generateId(), + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry2, + dynamoDBClient + ); + + const expectedDifferencesLength = 2; + const purposeDifferences = + await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map([ + [purpose1.id, platformStatesPurposeEntry1], + [purpose2.id, platformStatesPurposeEntry2], + ]), + purposesById: new Map([ + [purpose1.id, purpose1], + [purpose2.id, purpose2], + ]), + logger: genericLogger, + }); + expect(purposeDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the platform-states entry is missing and the purpose is not archived", async () => { + const purpose = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + await addOnePurpose(purpose); + + const expectedDifferencesLength = 1; + const purposeDifferences = + await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map(), + purposesById: new Map([[purpose.id, purpose]]), + logger: genericLogger, + }); + expect(purposeDifferences).toEqual(expectedDifferencesLength); + }); + + it("should not detect differences when the platform-states entry is missing and the purpose is archived", async () => { + const purpose = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.archived), + ]); + await addOnePurpose(purpose); + + const expectedDifferencesLength = 0; + const purposeDifferences = + await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map(), + purposesById: new Map([[purpose.id, purpose]]), + logger: genericLogger, + }); + expect(purposeDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the read model purpose is missing", async () => { + const purpose = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + + // platform-states + const purposeEntryPK = makePlatformStatesPurposePK(purpose.id); + const platformStatesPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPK, + state: itemState.active, + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const purposeDifferences = + await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map([ + [purpose.id, platformStatesPurposeEntry], + ]), + purposesById: new Map(), + logger: genericLogger, + }); + expect(purposeDifferences).toEqual(expectedDifferencesLength); + }); + }); + + describe("agreements", () => { + it("should not detect differences when the agreement platform-states entries are correct", async () => { + const agreement1: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement1); + + const agreement2: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement2); + + // platform-states + const agreementEntryPK1 = makePlatformStatesAgreementPK(agreement1.id); + const platformStatesAgreementEntry1: PlatformStatesAgreementEntry = { + PK: agreementEntryPK1, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement1.consumerId, + eserviceId: agreement1.eserviceId, + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement1.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement1.descriptorId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry1, + dynamoDBClient + ); + + const agreementEntryPK2 = makePlatformStatesAgreementPK(agreement2.id); + const platformStatesAgreementEntry2: PlatformStatesAgreementEntry = { + PK: agreementEntryPK2, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement2.consumerId, + eserviceId: agreement2.eserviceId, + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement2.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement2.descriptorId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry2, + dynamoDBClient + ); + + const expectedDifferencesLength = 0; + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map([ + [agreement1.id, platformStatesAgreementEntry1], + [agreement2.id, platformStatesAgreementEntry2], + ]), + agreementsById: new Map([ + [agreement1.id, agreement1], + [agreement2.id, agreement2], + ]), + logger: genericLogger, + }); + expect(agreementDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences for wrong agreement states", async () => { + const agreement1: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement1); + + const agreement2: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement2); + + // platform-states + const agreementEntryPK1 = makePlatformStatesAgreementPK(agreement1.id); + const platformStatesAgreementEntry1: PlatformStatesAgreementEntry = { + PK: agreementEntryPK1, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement1.consumerId, + eserviceId: agreement1.eserviceId, + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement1.stamps.activation!.when.toISOString(), + agreementDescriptorId: generateId(), + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry1, + dynamoDBClient + ); + + const agreementEntryPK2 = makePlatformStatesAgreementPK(agreement2.id); + const platformStatesAgreementEntry2: PlatformStatesAgreementEntry = { + PK: agreementEntryPK2, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement2.stamps.activation!.when.toISOString(), + agreementDescriptorId: generateId(), + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry2, + dynamoDBClient + ); + + const expectedDifferencesLength = 2; + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map([ + [agreement1.id, platformStatesAgreementEntry1], + [agreement2.id, platformStatesAgreementEntry2], + ]), + agreementsById: new Map([ + [agreement1.id, agreement1], + [agreement2.id, agreement2], + ]), + logger: genericLogger, + }); + expect(agreementDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the platform-states entry is missing and the agreement is not archived", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement); + + const expectedDifferencesLength = 1; + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map(), + agreementsById: new Map([[agreement.id, agreement]]), + logger: genericLogger, + }); + expect(agreementDifferences).toEqual(expectedDifferencesLength); + }); + + it("should not detect differences when the platform-states entry is missing and the agreement is archived", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement); + + const expectedDifferencesLength = 0; + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map(), + agreementsById: new Map([[agreement.id, agreement]]), + logger: genericLogger, + }); + expect(agreementDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the read model agreement is missing", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + + // platform-states + const agreementEntryPK = makePlatformStatesAgreementPK(agreement.id); + const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPK, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map([ + [agreement.id, platformStatesAgreementEntry], + ]), + agreementsById: new Map(), + logger: genericLogger, + }); + expect(agreementDifferences).toEqual(expectedDifferencesLength); + }); + }); + + describe("eservices", () => { + it("should not detect differences when the catalog platform-states entries are correct", async () => { + const descriptor1: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const descriptor2: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const descriptor3: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.deprecated, + audience: ["pagopa.it"], + }; + + const eservice1: EService = { + ...getMockEService(), + descriptors: [descriptor1], + }; + const eservice2: EService = { + ...getMockEService(), + descriptors: [descriptor2, descriptor3], + }; + await addOneEService(eservice1); + await addOneEService(eservice2); + + // platform-states + const catalogEntryPK1 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice1.id, + descriptorId: descriptor1.id, + }); + const platformStatesCatalogEntry1: PlatformStatesCatalogEntry = { + PK: catalogEntryPK1, + state: itemState.active, + descriptorAudience: descriptor1.audience, + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry1, + dynamoDBClient + ); + + const catalogEntryPK2 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice2.id, + descriptorId: descriptor2.id, + }); + const platformStatesCatalogEntry2: PlatformStatesCatalogEntry = { + PK: catalogEntryPK2, + state: itemState.active, + descriptorAudience: descriptor2.audience, + descriptorVoucherLifespan: descriptor2.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry2, + dynamoDBClient + ); + + const catalogEntryPK3 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice2.id, + descriptorId: descriptor3.id, + }); + const platformStatesCatalogEntry3: PlatformStatesCatalogEntry = { + PK: catalogEntryPK3, + state: itemState.active, + descriptorAudience: descriptor3.audience, + descriptorVoucherLifespan: descriptor3.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry3, + dynamoDBClient + ); + + const expectedDifferencesLength = 0; + const catalogDifferences = + await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map([ + [ + eservice1.id, + new Map([[descriptor1.id, platformStatesCatalogEntry1]]), + ], + [ + eservice2.id, + new Map([ + [descriptor2.id, platformStatesCatalogEntry2], + [descriptor3.id, platformStatesCatalogEntry3], + ]), + ], + ]), + eservicesById: new Map([ + [eservice1.id, eservice1], + [eservice2.id, eservice2], + ]), + logger: genericLogger, + }); + expect(catalogDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences for wrong eservice states", async () => { + const descriptor1: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const descriptor2: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + + const eservice1: EService = { + ...getMockEService(), + descriptors: [descriptor1], + }; + const eservice2: EService = { + ...getMockEService(), + descriptors: [descriptor2], + }; + await addOneEService(eservice1); + await addOneEService(eservice2); + + // platform-states + const catalogEntryPK1 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice1.id, + descriptorId: eservice1.descriptors[0].id, + }); + const platformStatesCatalogEntry1: PlatformStatesCatalogEntry = { + PK: catalogEntryPK1, + state: itemState.inactive, + descriptorAudience: descriptor1.audience, + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry1, + dynamoDBClient + ); + + const catalogEntryPK2 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice2.id, + descriptorId: eservice2.descriptors[0].id, + }); + const platformStatesCatalogEntry2: PlatformStatesCatalogEntry = { + PK: catalogEntryPK2, + state: itemState.inactive, + descriptorAudience: ["wrong-audience"], + descriptorVoucherLifespan: 1, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry2, + dynamoDBClient + ); + + const expectedDifferencesLength = 2; + const catalogDifferences = + await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map([ + [ + eservice1.id, + new Map([[descriptor1.id, platformStatesCatalogEntry1]]), + ], + [ + eservice2.id, + new Map([[descriptor2.id, platformStatesCatalogEntry2]]), + ], + ]), + eservicesById: new Map([ + [eservice1.id, eservice1], + [eservice2.id, eservice2], + ]), + logger: genericLogger, + }); + expect(catalogDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the platform-states entry is missing and the descriptor is not archived", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + await addOneEService(eservice); + + const expectedDifferencesLength = 1; + const catalogDifferences = + await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map(), + eservicesById: new Map([[eservice.id, eservice]]), + logger: genericLogger, + }); + expect(catalogDifferences).toEqual(expectedDifferencesLength); + }); + + it("should not detect differences when the platform-states entry is missing and the descriptor is archived", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.archived, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + await addOneEService(eservice); + + const expectedDifferencesLength = 0; + const catalogDifferences = + await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map(), + eservicesById: new Map([[eservice.id, eservice]]), + logger: genericLogger, + }); + expect(catalogDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the read model eservice is missing", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + + // platform-states + const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }); + const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { + PK: catalogEntryPK, + state: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const catalogDifferences = + await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map([ + [ + eservice.id, + new Map([[descriptor.id, platformStatesCatalogEntry]]), + ], + ]), + eservicesById: new Map(), + logger: genericLogger, + }); + expect(catalogDifferences).toEqual(expectedDifferencesLength); + }); + }); + + // The script doesn't compare the platform-states table with the read model for clients + describe("clients", () => { + it("should not detect differences when the client states are correct", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenGenStatesClientKidPurposePK, + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: itemState.active, + purposeVersionId: purpose.versions[0].id, + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 0; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences if the token-generation-states entry is incomplete", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenGenStatesClientKidPurposePK, + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + updatedAt: new Date().toISOString(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should not detect differences if the purpose state in the token-generation-state entry is inactive and the purpose is missing in the read model", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([ + getMockPurposeVersion(purposeVersionState.waitingForApproval), + ]), + eserviceId: eservice.id, + }; + const purposesById = new Map(); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenGenStatesClientKidPurposePK, + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: itemState.inactive, + purposeVersionId: purpose.versions[0].id, + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 0; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences if the purpose state in the token-generation-state entry is active and the purpose is missing in the read model", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map(); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenGenStatesClientKidPurposePK, + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: itemState.active, + purposeVersionId: purpose.versions[0].id, + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences if the token-generation-states entry has a CLIENTKID PK but should have a CLIENTKIDPURPOSE PK", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPK = makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: client.keys[0].kid, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesClientKidPK), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_consumerId_eserviceId: undefined, + agreementId: undefined, + agreementState: undefined, + GSIPK_eserviceId_descriptorId: undefined, + descriptorState: undefined, + descriptorVoucherLifespan: undefined, + GSIPK_purposeId: undefined, + purposeState: undefined, + purposeVersionId: undefined, + GSIPK_clientId_purposeId: undefined, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences if the token-generation-states entry has a CLIENTKIDPURPOSE PK but should have a CLIENTKID PK", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient( + tokenGenStatesClientKidPurposePK + ), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the token-generation-states entries are missing if the client has keys", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + + await addOneClient(client); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map(), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should not detect differences when the token-generation-states entries are missing if the client has no keys", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id, generateId()], + consumerId: purpose.consumerId, + }; + const clientsById = new Map([[client.id, client]]); + await addOneClient(client); + + const expectedDifferencesLength = 0; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map(), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + + it("should detect differences when the read model client is missing", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + consumerId: purpose.consumerId, + purposes: [purpose.id], + keys: [getMockKey()], + }; + + const clientEntryPK = makePlatformStatesClientPK(client.id); + const platformStatesClientEntry: PlatformStatesClientEntry = { + PK: clientEntryPK, + state: itemState.active, + clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), + clientConsumerId: client.consumerId, + clientPurposesIds: client.purposes, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformStatesClientEntry( + platformStatesClientEntry, + dynamoDBClient + ); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient( + tokenGenStatesClientKidPurposePK + ), + consumerId: purpose.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById: new Map(), + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + }); +}); diff --git a/packages/token-generation-readmodel-checker/test/tsconfig.json b/packages/token-generation-readmodel-checker/test/tsconfig.json new file mode 100644 index 0000000000..379a994d81 --- /dev/null +++ b/packages/token-generation-readmodel-checker/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["."] +} diff --git a/packages/token-generation-readmodel-checker/test/utils.test.ts b/packages/token-generation-readmodel-checker/test/utils.test.ts new file mode 100644 index 0000000000..07c6e9c0ff --- /dev/null +++ b/packages/token-generation-readmodel-checker/test/utils.test.ts @@ -0,0 +1,602 @@ +import crypto from "crypto"; +import { genericLogger } from "pagopa-interop-commons"; +import { + buildDynamoDBTables, + deleteDynamoDBTables, + getMockAgreement, + getMockClient, + getMockDescriptor, + getMockEService, + getMockKey, + getMockPlatformStatesClientEntry, + getMockPurpose, + getMockPurposeVersion, + getMockTokenGenStatesConsumerClient, + writePlatformCatalogEntry, + writeTokenGenStatesConsumerClient, +} from "pagopa-interop-commons-test"; +import { + Agreement, + agreementState, + Client, + clientKindTokenGenStates, + Descriptor, + descriptorState, + EService, + EServiceId, + generateId, + itemState, + makeGSIPKClientIdPurposeId, + makeGSIPKConsumerIdEServiceId, + makeGSIPKClientIdKid, + makePlatformStatesAgreementPK, + makePlatformStatesEServiceDescriptorPK, + makePlatformStatesPurposePK, + makeTokenGenerationStatesClientKidPurposePK, + PlatformStatesAgreementEntry, + PlatformStatesCatalogEntry, + PlatformStatesPurposeEntry, + Purpose, + PurposeVersion, + purposeVersionState, + TenantId, + TokenGenerationStatesConsumerClient, +} from "pagopa-interop-models"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { tokenGenerationReadModelServiceBuilder } from "../src/services/tokenGenerationReadModelService.js"; +import { + compareReadModelAgreementsWithPlatformStates, + compareReadModelClientsAndTokenGenStates, + compareReadModelEServicesWithPlatformStates, + compareReadModelPurposesWithPlatformStates, + getLastAgreement, + getLastPurposeVersion, + getValidDescriptors, +} from "../src/utils/utils.js"; +import { + addOneAgreement, + addOneClient, + addOneEService, + addOnePurpose, + dynamoDBClient, + writePlatformStatesClientEntry, +} from "./utils.js"; + +describe("Token Generation Read Model Checker utils tests", () => { + const tokenGenerationService = + tokenGenerationReadModelServiceBuilder(dynamoDBClient); + + beforeEach(async () => { + await buildDynamoDBTables(dynamoDBClient); + }); + afterEach(async () => { + await deleteDynamoDBTables(dynamoDBClient); + }); + + describe("purpose utils", () => { + it("compareReadModelPurposesWithPlatformStates", async () => { + const purpose1 = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + const purpose2 = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.active), + ]); + await addOnePurpose(purpose1); + await addOnePurpose(purpose2); + + // platform-states + const purposeEntryPrimaryKey1 = makePlatformStatesPurposePK(purpose1.id); + const platformPurposeEntry1: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey1, + state: itemState.active, + purposeVersionId: purpose1.versions[0].id, + purposeEserviceId: purpose1.eserviceId, + purposeConsumerId: purpose1.consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + + const purposeEntryPrimaryKey2 = makePlatformStatesPurposePK(purpose2.id); + const platformPurposeEntry2: PlatformStatesPurposeEntry = { + PK: purposeEntryPrimaryKey2, + state: itemState.active, + purposeVersionId: generateId(), + purposeEserviceId: generateId(), + purposeConsumerId: generateId(), + version: 1, + updatedAt: new Date().toISOString(), + }; + + const differences = await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map([ + [purpose1.id, platformPurposeEntry1], + [purpose2.id, platformPurposeEntry2], + ]), + purposesById: new Map([ + [purpose1.id, purpose1], + [purpose2.id, purpose2], + ]), + logger: genericLogger, + }); + expect(differences).toEqual(1); + }); + }); + + describe("agreement utils", () => { + it("compareReadModelAgreementsWithPlatformStates", async () => { + const agreement1: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreement2: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement1); + await addOneAgreement(agreement2); + + // platform-states + const agreementEntryPrimaryKey1 = makePlatformStatesAgreementPK( + agreement1.id + ); + const platformAgreementEntry1: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey1, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement1.consumerId, + eserviceId: agreement1.eserviceId, + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement1.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement1.descriptorId, + version: 1, + updatedAt: new Date().toISOString(), + }; + + const agreementEntryPrimaryKey2 = makePlatformStatesAgreementPK( + agreement2.id + ); + const platformAgreementEntry2: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey2, + state: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: generateId(), + eserviceId: generateId(), + }), + GSISK_agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement2.stamps.activation!.when.toISOString(), + agreementDescriptorId: generateId(), + version: 1, + updatedAt: new Date().toISOString(), + }; + + const differences = await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map([ + [agreement1.id, platformAgreementEntry1], + [agreement2.id, platformAgreementEntry2], + ]), + agreementsById: new Map([ + [agreement1.id, agreement1], + [agreement2.id, agreement2], + ]), + logger: genericLogger, + }); + expect(differences).toEqual(1); + }); + }); + + describe("catalog utils", () => { + it("compareReadModelEServicesWithPlatformStates", async () => { + const descriptor1: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const descriptor2: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice1 = { + ...getMockEService(), + descriptors: [descriptor1], + }; + const eservice2 = { + ...getMockEService(), + descriptors: [descriptor2], + }; + await addOneEService(eservice1); + await addOneEService(eservice2); + + // platform-states + const catalogEntryPrimaryKey1 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice1.id, + descriptorId: descriptor1.id, + }); + const platformCatalogEntry1: PlatformStatesCatalogEntry = { + PK: catalogEntryPrimaryKey1, + state: itemState.active, + descriptorAudience: descriptor1.audience, + descriptorVoucherLifespan: descriptor1.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + + const catalogEntryPrimaryKey2 = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice2.id, + descriptorId: descriptor2.id, + }); + const platformCatalogEntry2: PlatformStatesCatalogEntry = { + PK: catalogEntryPrimaryKey2, + state: itemState.inactive, + descriptorAudience: ["wrong-audience"], + descriptorVoucherLifespan: 1, + version: 1, + updatedAt: new Date().toISOString(), + }; + + const differences = await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map([ + [eservice1.id, new Map([[descriptor1.id, platformCatalogEntry1]])], + [eservice2.id, new Map([[descriptor2.id, platformCatalogEntry2]])], + ]), + eservicesById: new Map([ + [eservice1.id, eservice1], + [eservice2.id, eservice2], + ]), + logger: genericLogger, + }); + expect(differences).toEqual(1); + }); + }); + + describe("client and token-generation-states utils", () => { + it("compareReadModelClientsAndTokenGenStates", async () => { + const descriptor1: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const descriptor2: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["test.pagopa.it"], + }; + const eservice1: EService = { + ...getMockEService(), + descriptors: [descriptor1], + }; + const eservice2: EService = { + ...getMockEService(), + descriptors: [descriptor2], + }; + const eservicesById = new Map([ + [eservice1.id, eservice1], + [eservice2.id, eservice2], + ]); + await addOneEService(eservice1); + await addOneEService(eservice2); + + const purpose1: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice1.id, + }; + const purpose2: Purpose = { + ...getMockPurpose([ + getMockPurposeVersion(purposeVersionState.archived), + ]), + eserviceId: eservice2.id, + }; + const purposesById = new Map([ + [purpose1.id, purpose1], + [purpose2.id, purpose2], + ]); + await addOnePurpose(purpose1); + await addOnePurpose(purpose2); + + const agreement1: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose1.consumerId, + eserviceId: eservice1.id, + descriptorId: descriptor1.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreement2: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose2.consumerId, + eserviceId: eservice2.id, + descriptorId: descriptor2.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose1.consumerId, + eserviceId: eservice1.id, + }), + [agreement1], + ], + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose2.consumerId, + eserviceId: eservice2.id, + }), + [agreement2], + ], + ]); + await addOneAgreement(agreement1); + await addOneAgreement(agreement2); + + const client1: Client = { + ...getMockClient(), + purposes: [purpose1.id], + consumerId: purpose1.consumerId, + keys: [getMockKey()], + }; + const client2: Client = { + ...getMockClient(), + purposes: [purpose2.id], + consumerId: purpose2.consumerId, + keys: [getMockKey()], + }; + const clientsById = new Map([ + [client1.id, client1], + [client2.id, client2], + ]); + await addOneClient(client1); + await addOneClient(client2); + + // token-generation-states + const tokenGenStatesEntryPK = makeTokenGenerationStatesClientKidPurposePK( + { + clientId: client1.id, + kid: client1.keys[0].kid, + purposeId: purpose1.id, + } + ); + const tokenGenStatesEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + consumerId: purpose1.consumerId, + GSIPK_clientId: generateId(), + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client1.id, + kid: client1.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client1.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client1.id, + purposeId: purpose1.id, + }), + }; + + const differences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([[client1.id, [tokenGenStatesEntry]]]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(differences).toEqual(2); + }); + }); + + describe("readAllTokenGenerationStatesItems", () => { + it("no need for pagination", async () => { + const tokenEntriesLength = 2; + + const tokenGenStatesEntry1 = getMockTokenGenStatesConsumerClient(); + await writeTokenGenStatesConsumerClient( + tokenGenStatesEntry1, + dynamoDBClient + ); + + const tokenGenStatesEntry2: TokenGenerationStatesConsumerClient = + getMockTokenGenStatesConsumerClient(); + await writeTokenGenStatesConsumerClient( + tokenGenStatesEntry2, + dynamoDBClient + ); + + vi.spyOn(dynamoDBClient, "send"); + const tokenEntries = + await tokenGenerationService.readAllTokenGenerationStatesItems(); + + expect(dynamoDBClient.send).toHaveBeenCalledTimes(1); + expect(tokenEntries).toHaveLength(tokenEntriesLength); + expect(tokenEntries).toEqual( + expect.arrayContaining([tokenGenStatesEntry1, tokenGenStatesEntry2]) + ); + }); + + it("with pagination", async () => { + const tokenEntriesLength = 10; + + const writtenEntries: TokenGenerationStatesConsumerClient[] = []; + // eslint-disable-next-line functional/no-let + for (let i = 0; i < tokenEntriesLength; i++) { + const tokenGenStatesEntryPK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesEntry: TokenGenerationStatesConsumerClient = { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK), + publicKey: crypto.randomBytes(100000).toString("hex"), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesEntry, + dynamoDBClient + ); + // eslint-disable-next-line functional/immutable-data + writtenEntries.push(tokenGenStatesEntry); + } + vi.spyOn(dynamoDBClient, "send"); + const tokenEntries = + await tokenGenerationService.readAllTokenGenerationStatesItems(); + + expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); + expect(tokenEntries).toHaveLength(tokenEntriesLength); + expect(tokenEntries).toEqual(expect.arrayContaining(writtenEntries)); + }); + }); + + describe("readAllPlatformStatesItems", () => { + it("no need for pagination", async () => { + const platformStatesEntriesLength = 2; + + const platformStatesEntry1 = getMockPlatformStatesClientEntry(); + await writePlatformStatesClientEntry( + platformStatesEntry1, + dynamoDBClient + ); + + const platformStatesEntry2 = getMockPlatformStatesClientEntry(); + await writePlatformStatesClientEntry( + platformStatesEntry2, + dynamoDBClient + ); + + vi.spyOn(dynamoDBClient, "send"); + const platformStatesEntries = + await tokenGenerationService.readAllPlatformStatesItems(); + + expect(dynamoDBClient.send).toHaveBeenCalledTimes(1); + expect(platformStatesEntries).toHaveLength(platformStatesEntriesLength); + expect(platformStatesEntries).toEqual( + expect.arrayContaining([platformStatesEntry1, platformStatesEntry2]) + ); + }); + + it("with pagination", async () => { + const platformStatesEntriesLength = 10; + + const writtenEntries: PlatformStatesCatalogEntry[] = []; + // eslint-disable-next-line functional/no-let + for (let i = 0; i < platformStatesEntriesLength; i++) { + const platformStatesEntry: PlatformStatesCatalogEntry = { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: generateId(), + descriptorId: generateId(), + }), + state: itemState.active, + descriptorAudience: [crypto.randomBytes(100000).toString("hex")], + descriptorVoucherLifespan: 60, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry(platformStatesEntry, dynamoDBClient); + // eslint-disable-next-line functional/immutable-data + writtenEntries.push(platformStatesEntry); + } + vi.spyOn(dynamoDBClient, "send"); + const platformStatesEntries = + await tokenGenerationService.readAllPlatformStatesItems(); + + expect(dynamoDBClient.send).toHaveBeenCalledTimes(2); + expect(platformStatesEntries).toHaveLength(platformStatesEntriesLength); + expect(platformStatesEntries).toEqual( + expect.arrayContaining(writtenEntries) + ); + }); + }); + + it("getLastPurposeVersion", () => { + const date1 = new Date(); + const date2 = new Date(); + const date3 = new Date(); + date2.setDate(date1.getDate() + 1); + date3.setDate(date1.getDate() + 2); + const purposeVersion1: PurposeVersion = { + ...getMockPurposeVersion(purposeVersionState.archived), + createdAt: date1, + }; + const purposeVersion2: PurposeVersion = { + ...getMockPurposeVersion(purposeVersionState.active), + createdAt: date2, + }; + const purposeVersion3: PurposeVersion = { + ...getMockPurposeVersion(purposeVersionState.rejected), + createdAt: date3, + }; + + expect( + getLastPurposeVersion([purposeVersion1, purposeVersion2, purposeVersion3]) + ).toEqual(purposeVersion2); + }); + + it("getLastAgreement", () => { + const date1 = new Date(); + const date2 = new Date(); + date2.setDate(date1.getDate() + 1); + + const eserviceId = generateId(); + const consumerId = generateId(); + const agreement1: Agreement = { + ...getMockAgreement(eserviceId, consumerId, agreementState.active), + createdAt: date1, + }; + const agreement2: Agreement = { + ...getMockAgreement(eserviceId, consumerId, agreementState.pending), + createdAt: date2, + }; + + expect(getLastAgreement([agreement1, agreement2])).toEqual(agreement1); + }); + + it("getValidDescriptors", () => { + const publishedDescriptor = getMockDescriptor(descriptorState.published); + const waitingForApprovalDescriptor = getMockDescriptor( + descriptorState.waitingForApproval + ); + const deprecatedDescriptor = getMockDescriptor(descriptorState.deprecated); + const archivedDescriptor = getMockDescriptor(descriptorState.archived); + const suspendedDescriptor = getMockDescriptor(descriptorState.suspended); + const draftDescriptor = getMockDescriptor(descriptorState.draft); + + expect( + getValidDescriptors([ + publishedDescriptor, + waitingForApprovalDescriptor, + deprecatedDescriptor, + archivedDescriptor, + suspendedDescriptor, + draftDescriptor, + ]) + ).toEqual( + expect.arrayContaining([ + publishedDescriptor, + deprecatedDescriptor, + suspendedDescriptor, + ]) + ); + }); +}); diff --git a/packages/token-generation-readmodel-checker/test/utils.ts b/packages/token-generation-readmodel-checker/test/utils.ts new file mode 100644 index 0000000000..eac8ae7b6c --- /dev/null +++ b/packages/token-generation-readmodel-checker/test/utils.ts @@ -0,0 +1,102 @@ +import { + DynamoDBClient, + PutItemInput, + PutItemCommand, +} from "@aws-sdk/client-dynamodb"; +import { + setupTestContainersVitest, + writeInReadmodel, +} from "pagopa-interop-commons-test"; +import { + Agreement, + Client, + EService, + PlatformStatesClientEntry, + Purpose, + toReadModelAgreement, + toReadModelClient, + toReadModelEService, + toReadModelPurpose, +} from "pagopa-interop-models"; +import { afterEach, inject } from "vitest"; + +export const config = inject("tokenGenerationReadModelConfig"); + +export const { cleanup, readModelRepository } = await setupTestContainersVitest( + inject("readModelConfig") +); + +afterEach(cleanup); + +if (!config) { + throw new Error("config is not defined"); +} + +export const dynamoDBClient = new DynamoDBClient({ + endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, +}); + +export const addOneEService = async (eservice: EService): Promise => { + await writeInReadmodel( + toReadModelEService(eservice), + readModelRepository.eservices + ); +}; + +export const addOnePurpose = async (purpose: Purpose): Promise => { + await writeInReadmodel( + toReadModelPurpose(purpose), + readModelRepository.purposes + ); +}; + +export const addOneAgreement = async (agreement: Agreement): Promise => { + await writeInReadmodel( + toReadModelAgreement(agreement), + readModelRepository.agreements + ); +}; + +export const addOneClient = async (client: Client): Promise => { + await writeInReadmodel( + toReadModelClient(client), + readModelRepository.clients + ); +}; + +export const writePlatformStatesClientEntry = async ( + clientEntry: PlatformStatesClientEntry, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: PutItemInput = { + ConditionExpression: "attribute_not_exists(PK)", + Item: { + PK: { + S: clientEntry.PK, + }, + state: { + S: clientEntry.state, + }, + clientPurposesIds: { + L: clientEntry.clientPurposesIds.map((purposeId) => ({ + S: purposeId, + })), + }, + clientKind: { + S: clientEntry.clientKind, + }, + clientConsumerId: { + S: clientEntry.clientConsumerId, + }, + version: { + N: clientEntry.version.toString(), + }, + updatedAt: { + S: clientEntry.updatedAt, + }, + }, + TableName: "platform-states", + }; + const command = new PutItemCommand(input); + await dynamoDBClient.send(command); +}; diff --git a/packages/token-generation-readmodel-checker/test/vitestGlobalSetup.ts b/packages/token-generation-readmodel-checker/test/vitestGlobalSetup.ts new file mode 100644 index 0000000000..32972b5c32 --- /dev/null +++ b/packages/token-generation-readmodel-checker/test/vitestGlobalSetup.ts @@ -0,0 +1,3 @@ +import { setupTestContainersVitestGlobal } from "pagopa-interop-commons-test"; + +export default setupTestContainersVitestGlobal(); diff --git a/packages/token-generation-readmodel-checker/tsconfig.check.json b/packages/token-generation-readmodel-checker/tsconfig.check.json new file mode 100644 index 0000000000..a19f84bcb7 --- /dev/null +++ b/packages/token-generation-readmodel-checker/tsconfig.check.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": true, + }, + "include": ["src", "test"] +} diff --git a/packages/token-generation-readmodel-checker/tsconfig.json b/packages/token-generation-readmodel-checker/tsconfig.json new file mode 100644 index 0000000000..a1ec44f6e6 --- /dev/null +++ b/packages/token-generation-readmodel-checker/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": [ + "src" + ] +} diff --git a/packages/token-generation-readmodel-checker/vitest.config.ts b/packages/token-generation-readmodel-checker/vitest.config.ts new file mode 100644 index 0000000000..9ece1be991 --- /dev/null +++ b/packages/token-generation-readmodel-checker/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 7754fb20e2..1c1c5c4482 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3193,6 +3193,61 @@ importers: specifier: 1.6.0 version: 1.6.0(@types/node@20.14.6) + packages/token-generation-readmodel-checker: + dependencies: + '@aws-sdk/client-dynamodb': + specifier: 3.693.0 + version: 3.693.0 + '@aws-sdk/util-dynamodb': + specifier: 3.693.0 + version: 3.693.0(@aws-sdk/client-dynamodb@3.693.0) + dotenv-flow: + specifier: 4.1.0 + version: 4.1.0 + json-diff: + specifier: 1.0.6 + version: 1.0.6 + pagopa-interop-commons: + specifier: workspace:* + version: link:../commons + pagopa-interop-models: + specifier: workspace:* + version: link:../models + ts-pattern: + specifier: 5.2.0 + version: 5.2.0 + zod: + specifier: 3.23.8 + version: 3.23.8 + devDependencies: + '@pagopa/eslint-config': + specifier: 3.0.0 + version: 3.0.0(tsutils@3.21.0(typescript@5.4.5))(typescript@5.4.5) + '@types/json-diff': + specifier: 1.0.3 + version: 1.0.3 + '@types/node': + specifier: 20.14.6 + version: 20.14.6 + pagopa-interop-commons-test: + specifier: workspace:* + version: link:../commons-test + prettier: + specifier: 2.8.8 + version: 2.8.8 + testcontainers: + specifier: 10.9.0 + version: 10.9.0 + tsx: + specifier: 4.19.1 + version: 4.19.1 + typescript: + specifier: 5.4.5 + version: 5.4.5 + vitest: + specifier: 1.6.0 + version: 1.6.0(@types/node@20.14.6) + packages: '@ampproject/remapping@2.3.0': @@ -7986,19 +8041,19 @@ snapshots: '@smithy/config-resolver': 3.0.9 '@smithy/core': 2.4.8 '@smithy/fetch-http-handler': 3.2.9 - '@smithy/hash-node': 3.0.7 - '@smithy/invalid-dependency': 3.0.7 - '@smithy/middleware-content-length': 3.0.9 - '@smithy/middleware-endpoint': 3.1.4 - '@smithy/middleware-retry': 3.0.23 - '@smithy/middleware-serde': 3.0.7 - '@smithy/middleware-stack': 3.0.7 - '@smithy/node-config-provider': 3.1.8 - '@smithy/node-http-handler': 3.2.4 - '@smithy/protocol-http': 4.1.4 - '@smithy/smithy-client': 3.4.0 - '@smithy/types': 3.5.0 - '@smithy/url-parser': 3.0.7 + '@smithy/hash-node': 3.0.10 + '@smithy/invalid-dependency': 3.0.10 + '@smithy/middleware-content-length': 3.0.12 + '@smithy/middleware-endpoint': 3.2.4 + '@smithy/middleware-retry': 3.0.28 + '@smithy/middleware-serde': 3.0.10 + '@smithy/middleware-stack': 3.0.10 + '@smithy/node-config-provider': 3.1.11 + '@smithy/node-http-handler': 3.3.1 + '@smithy/protocol-http': 4.1.7 + '@smithy/smithy-client': 3.4.5 + '@smithy/types': 3.7.1 + '@smithy/url-parser': 3.0.10 '@smithy/util-base64': 3.0.0 '@smithy/util-body-length-browser': 3.0.0 '@smithy/util-body-length-node': 3.0.0 From f6c9829b2f6d01a4374cd6361307f678fb55a4fe Mon Sep 17 00:00:00 2001 From: Alessio Gallitano <25105748+galales@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:44:53 +0100 Subject: [PATCH 109/126] Platformstate writers idempotence (#1362) Co-authored-by: Stefano Hu <76391491+shuyec@users.noreply.github.com> --- .../src/consumerServiceV1.ts | 29 ++-- .../src/utils.ts | 145 ++---------------- .../test/utils.test.ts | 6 +- 3 files changed, 39 insertions(+), 141 deletions(-) diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index 484f3777e9..c6e4476930 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -23,6 +23,7 @@ import { TokenGenerationStatesApiClient, TokenGenerationStatesConsumerClient, unsafeBrandId, + TokenGenerationStatesClientKidPK, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { Logger } from "pagopa-interop-commons"; @@ -34,7 +35,7 @@ import { deleteClientEntryFromTokenGenerationStates, deleteEntriesFromTokenGenStatesByClientIdKidV1, deleteEntriesFromTokenGenStatesByClientIdV1, - deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1, + deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1, extractAgreementIdFromAgreementPK, extractKidFromTokenGenStatesEntryPK, readConsumerClientsInTokenGenStatesV1, @@ -305,6 +306,7 @@ export async function handleMessageV1( ); } }) + // eslint-disable-next-line sonarjs/cognitive-complexity .with({ type: "ClientPurposeAdded" }, async (msg) => { const clientId = unsafeBrandId(msg.data.clientId); @@ -328,7 +330,10 @@ export async function handleMessageV1( return Promise.resolve(); } - const purposeIds = [...clientEntry.clientPurposesIds, purposeId]; + // Deduplicate in case of retry and reprocess + const purposeIds = Array.from( + new Set([...clientEntry.clientPurposesIds, purposeId]) + ); await setClientPurposeIdsInPlatformStatesEntry( { primaryKey: pk, @@ -360,7 +365,8 @@ export async function handleMessageV1( for (const entry of tokenGenStatesConsumerClients) { const addedTokenGenStatesConsumerClient = await match( - clientEntry.clientPurposesIds.length + // Count without the current purpose + purposeIds.length - 1 ) .with(0, async () => { const newTokenGenStatesConsumerClient = @@ -380,11 +386,16 @@ export async function handleMessageV1( dynamoDBClient, logger ); - await deleteClientEntryFromTokenGenerationStates( - entry.PK, - dynamoDBClient, - logger - ); + if ( + TokenGenerationStatesClientKidPK.safeParse(entry.PK).success + ) { + // Remove only partial entries (to avoid deleting complete entries after retry) + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient, + logger + ); + } return newTokenGenStatesConsumerClient; }) .with(P.number.gt(0), async () => { @@ -482,7 +493,7 @@ export async function handleMessageV1( // token-generation-states if (updatedPurposeIds.length > 0) { - await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1( + await deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1( GSIPK_clientId_purposeId, dynamoDBClient, logger diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index 9fb3e49e53..f2f421cf7c 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -309,7 +309,7 @@ export const readPlatformClientEntry = async ( } }; -const readTokenGenStatesConsumerClientsByGSIPKClientPurposeV1 = async ( +const readTokenGenStatesConsumerClientsByClientPurposeV1 = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record @@ -381,7 +381,7 @@ export const deleteEntriesFromTokenGenStatesByClientIdPurposeIdV2 = async ( ); }; -export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1 = async ( +export const deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1 = async ( GSIPK_clientId_purposeId: GSIPKClientIdPurposeId, dynamoDBClient: DynamoDBClient, logger: Logger @@ -391,7 +391,7 @@ export const deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1 = async ( dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { - const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurposeV1( + const res = await readTokenGenStatesConsumerClientsByClientPurposeV1( GSIPK_clientId_purposeId, dynamoDBClient, exclusiveStartKey @@ -426,7 +426,7 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( dynamoDBClient: DynamoDBClient, exclusiveStartKey?: Record ): Promise => { - const res = await readTokenGenStatesConsumerClientsByGSIPKClientPurposeV1( + const res = await readTokenGenStatesConsumerClientsByClientPurposeV1( GSIPK_clientId_purposeId, dynamoDBClient, exclusiveStartKey @@ -448,14 +448,21 @@ export const convertEntriesToClientKidInTokenGenerationStates = async ( }; // write the new one - await writeTokenGenStatesConsumerClient(newEntry, dynamoDBClient, logger); - - // delete the old one - await deleteClientEntryFromTokenGenerationStates( - entry.PK, + await upsertTokenGenStatesConsumerClient( + newEntry, dynamoDBClient, logger ); + + if (TokenGenerationStatesClientKidPurposePK.safeParse(entry.PK).success) { + // Remove only complete entries (to avoid deleting partial entries after retry) + // delete the old one + await deleteClientEntryFromTokenGenerationStates( + entry.PK, + dynamoDBClient, + logger + ); + } } if (!res.lastEvaluatedKey) { @@ -730,126 +737,6 @@ export const upsertTokenGenStatesConsumerClient = async ( ); }; -export const writeTokenGenStatesConsumerClient = async ( - tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient, - dynamoDBClient: DynamoDBClient, - logger: Logger -): Promise => { - const input: PutItemInput = { - ConditionExpression: "attribute_not_exists(PK)", - Item: { - PK: { - S: tokenGenStatesConsumerClient.PK, - }, - ...(tokenGenStatesConsumerClient.descriptorState - ? { - descriptorState: { - S: tokenGenStatesConsumerClient.descriptorState, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.descriptorAudience - ? { - descriptorAudience: { - L: tokenGenStatesConsumerClient.descriptorAudience.map( - (item) => ({ - S: item, - }) - ), - }, - } - : {}), - ...(tokenGenStatesConsumerClient.descriptorVoucherLifespan - ? { - descriptorVoucherLifespan: { - N: tokenGenStatesConsumerClient.descriptorVoucherLifespan.toString(), - }, - } - : {}), - updatedAt: { - S: tokenGenStatesConsumerClient.updatedAt, - }, - consumerId: { - S: tokenGenStatesConsumerClient.consumerId, - }, - ...(tokenGenStatesConsumerClient.agreementId - ? { - agreementId: { - S: tokenGenStatesConsumerClient.agreementId, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.purposeVersionId - ? { - purposeVersionId: { - S: tokenGenStatesConsumerClient.purposeVersionId, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.GSIPK_consumerId_eserviceId - ? { - GSIPK_consumerId_eserviceId: { - S: tokenGenStatesConsumerClient.GSIPK_consumerId_eserviceId, - }, - } - : {}), - clientKind: { - S: tokenGenStatesConsumerClient.clientKind, - }, - publicKey: { - S: tokenGenStatesConsumerClient.publicKey, - }, - GSIPK_clientId: { - S: tokenGenStatesConsumerClient.GSIPK_clientId, - }, - GSIPK_clientId_kid: { - S: tokenGenStatesConsumerClient.GSIPK_clientId_kid, - }, - ...(tokenGenStatesConsumerClient.GSIPK_clientId_purposeId - ? { - GSIPK_clientId_purposeId: { - S: tokenGenStatesConsumerClient.GSIPK_clientId_purposeId, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.agreementState - ? { - agreementState: { - S: tokenGenStatesConsumerClient.agreementState, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.GSIPK_eserviceId_descriptorId - ? { - GSIPK_eserviceId_descriptorId: { - S: tokenGenStatesConsumerClient.GSIPK_eserviceId_descriptorId, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.GSIPK_purposeId - ? { - GSIPK_purposeId: { - S: tokenGenStatesConsumerClient.GSIPK_purposeId, - }, - } - : {}), - ...(tokenGenStatesConsumerClient.purposeState - ? { - purposeState: { - S: tokenGenStatesConsumerClient.purposeState, - }, - } - : {}), - }, - TableName: config.tokenGenerationReadModelTableNameTokenGeneration, - }; - const command = new PutItemCommand(input); - await dynamoDBClient.send(command); - logger.info( - `Token-generation-states. Written consumer client ${tokenGenStatesConsumerClient.PK}` - ); -}; - export const clientKindToTokenGenerationStatesClientKind = ( kind: ClientKind ): ClientKindTokenGenStates => diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 0ae784364d..6c03fb3777 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -81,7 +81,7 @@ import { writePlatformClientEntry, deleteClientEntryFromTokenGenerationStates, readPlatformClientEntry, - deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1, + deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1, upsertTokenGenStatesConsumerClient, upsertTokenGenStatesApiClient, deleteEntriesFromTokenGenStatesByClientIdV2, @@ -601,7 +601,7 @@ describe("utils", () => { expect(res).toEqual(clientEntry1); }); - it("deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1", async () => { + it("deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1", async () => { const GSIPK_clientId_purposeId = makeGSIPKClientIdPurposeId({ clientId: generateId(), purposeId: generateId(), @@ -631,7 +631,7 @@ describe("utils", () => { dynamoDBClient ); - await deleteEntriesFromTokenGenStatesByGSIPKClientIdPurposeIdV1( + await deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1( GSIPK_clientId_purposeId, dynamoDBClient, genericLogger From 3f6b0ee64326bbf75259a31584625cc1c98544dc Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Tue, 14 Jan 2025 15:24:25 +0100 Subject: [PATCH 110/126] IMN-787 Fix config in token-details-persister (#1330) Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- packages/kafka-iam-auth/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/kafka-iam-auth/src/index.ts b/packages/kafka-iam-auth/src/index.ts index 6ffb6d763f..4790d382ab 100644 --- a/packages/kafka-iam-auth/src/index.ts +++ b/packages/kafka-iam-auth/src/index.ts @@ -261,7 +261,10 @@ const initCustomConsumer = async ({ return Promise.resolve(false); }, }, - ...batchConsumerConfig, + maxWaitTimeInMs: batchConsumerConfig?.maxWaitKafkaBatchMillis, + minBytes: batchConsumerConfig?.minBytes, + maxBytes: batchConsumerConfig?.maxBytes, + sessionTimeout: batchConsumerConfig?.sessionTimeoutMillis, }); if (config.resetConsumerOffsets) { From 07db1bc182df51d4b150cdd4a285fd09c34b2197 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:43:36 +0100 Subject: [PATCH 111/126] Fix token-generation-readmodel-checker (#1363) Co-authored-by: Alessio Gallitano <25105748+galales@users.noreply.github.com> --- .../src/utils/utils.ts | 122 ++++++---- .../tokenGenerationReadModelChecker.test.ts | 211 +++++++++++++++++- 2 files changed, 282 insertions(+), 51 deletions(-) diff --git a/packages/token-generation-readmodel-checker/src/utils/utils.ts b/packages/token-generation-readmodel-checker/src/utils/utils.ts index bb9578fbe8..dc62d70ad6 100644 --- a/packages/token-generation-readmodel-checker/src/utils/utils.ts +++ b/packages/token-generation-readmodel-checker/src/utils/utils.ts @@ -638,13 +638,19 @@ function validateTokenGenerationStates({ agreementsByConsumerIdEserviceId: Map; logger: Logger; }): number { - const expectedTokenGenStatesEntriesCount = + // eslint-disable-next-line functional/no-let + let expectedTokenGenStatesEntriesCount = client.purposes.length > 0 ? client.keys.length * client.purposes.length : client.keys.length; if (!tokenGenStatesEntries || tokenGenStatesEntries.length === 0) { - if (client.keys.length === 0) { + if (client.keys.length === 0 || client.purposes.length === 0) { + /* + In the token-generation-states table it's possible for the consumer clients to have CLIENTKID PKs if the associated client has keys but not purposes. + This only happens for the events V1, but the script is not able to differentiate the entries based on the data provided, so all the entries of that type + are ignored in the token-generation-states check. + */ return 0; } @@ -658,57 +664,18 @@ function validateTokenGenerationStates({ // eslint-disable-next-line functional/no-let let differencesCount = 0; + // eslint-disable-next-line functional/no-let + let correctCount = 0; for (const e of tokenGenStatesEntries) { const extractedKid = getKidFromTokenGenStatesPK(e.PK); const key = client.keys.find( (k) => k.encodedPem === e.publicKey && k.kid === extractedKid ); - function compareClientKidEntry(): void { - if ( - !TokenGenerationStatesClientKidPK.safeParse(e.PK).success && - e.clientKind === clientKindTokenGenStates.consumer - ) { - logger.error( - `token-generation-states entry has PK ${e.PK}, but should have a CLIENTKID PK` - ); - } - - const comparisonTokenGenStatesEntry: ComparisonTokenGenStatesGenericClient = - { - PK: key - ? makeTokenGenerationStatesClientKidPK({ - clientId: client.id, - kid: key.kid, - }) - : undefined, - consumerId: client.consumerId, - clientKind: clientKindToTokenGenerationStatesClientKind(client.kind), - publicKey: key?.encodedPem, - GSIPK_clientId: client.id, - GSIPK_clientId_kid: key - ? makeGSIPKClientIdKid({ clientId: client.id, kid: key.kid }) - : undefined, - }; - - const objectsDiff = diff( - ComparisonTokenGenStatesGenericClient.parse(e), - comparisonTokenGenStatesEntry, - { sort: true } - ); - if (objectsDiff) { - differencesCount++; - logger.error( - `Differences in token-generation-states when checking entry with PK ${e.PK}` - ); - logger.error(JSON.stringify(objectsDiff, null, 2)); - } - } - match(e) .with({ clientKind: clientKindTokenGenStates.consumer }, (e) => { - if (client.purposes.length !== 0) { - // TokenGenerationStatesConsumerClient with CLIENTKIDPURPOSE PK + if (client.purposes.length > 0) { + // TokenGenerationStatesConsumerClient should have a CLIENTKIDPURPOSE PK const purposeId = getPurposeIdFromTokenGenStatesPK(e.PK); const purpose = purposeId ? purposesById.get(purposeId) : undefined; if (!purpose) { @@ -716,6 +683,8 @@ function validateTokenGenerationStates({ TokenGenerationStatesClientKidPurposePK.safeParse(e.PK).success ) { if (!e.purposeState || e.purposeState === itemState.inactive) { + // Ignore consumer clients with purpose state inactive if the purpose is not found in the read model + expectedTokenGenStatesEntriesCount--; return; } @@ -834,19 +803,74 @@ function validateTokenGenerationStates({ `Differences in token-generation-states when checking entry with PK ${e.PK}` ); logger.error(JSON.stringify(objectsDiff, null, 2)); + } else { + correctCount++; } } else { - // TokenGenerationStatesConsumerClient with CLIENTKID PK - compareClientKidEntry(); + // TokenGenerationStatesConsumerClient should have a CLIENTKID PK + if (TokenGenerationStatesClientKidPurposePK.safeParse(e.PK).success) { + logger.error( + `token-generation-states should have ${expectedTokenGenStatesEntriesCount} entries, but has a consumer client with PK ${e.PK}` + ); + differencesCount++; + } else { + // Ignore consumer clients with CLIENTKID PK + expectedTokenGenStatesEntriesCount--; + } } }) .with({ clientKind: clientKindTokenGenStates.api }, () => { - compareClientKidEntry(); + const comparisonTokenGenStatesEntry: ComparisonTokenGenStatesGenericClient = + { + PK: key + ? makeTokenGenerationStatesClientKidPK({ + clientId: client.id, + kid: key.kid, + }) + : undefined, + consumerId: client.consumerId, + clientKind: clientKindToTokenGenerationStatesClientKind( + client.kind + ), + publicKey: key?.encodedPem, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: key + ? makeGSIPKClientIdKid({ clientId: client.id, kid: key.kid }) + : undefined, + }; + + const objectsDiff = diff( + ComparisonTokenGenStatesGenericClient.parse(e), + comparisonTokenGenStatesEntry, + { sort: true } + ); + if (objectsDiff) { + differencesCount++; + logger.error( + `Differences in token-generation-states when checking entry with PK ${e.PK}` + ); + logger.error(JSON.stringify(objectsDiff, null, 2)); + } else { + correctCount++; + } }) .exhaustive(); } - return differencesCount; + const missingEntriesCount = expectedTokenGenStatesEntriesCount - correctCount; + return ( + missingEntriesCount + + differencesCount - + /* + This calculates the overlap between the expected count and the differences count. + Examples: + 1) 1 missing entry but 3 wrong entries, only 1 wrong entry can "replace" the missing entry. The other 2 wrong entries remain wrong. + -> Overlap = min(1, 3) = 1. + 2) 2 missing entries and 1 wrong entry, only 1 missing entry can be "replaced" by the wrong entry. The other missing entry remains missing. + -> Overlap = min(2, 1) = 1. + */ + Math.min(missingEntriesCount, differencesCount) + ); } export const agreementStateToItemState = (state: AgreementState): ItemState => diff --git a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts index 660c7bea37..eb0186da8f 100644 --- a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts +++ b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts @@ -243,7 +243,7 @@ describe("Token Generation Read Model Checker tests", () => { dynamoDBClient ); - const expectedDifferencesLength = 4; + const expectedDifferencesLength = 5; const differencesCount = await compareTokenGenerationReadModel( dynamoDBClient, genericLogger @@ -1082,6 +1082,118 @@ describe("Token Generation Read Model Checker tests", () => { expect(clientDifferences).toEqual(expectedDifferencesLength); }); + it("should detect differences when the token-generation-states entries length is wrong", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [purpose.id], + consumerId: purpose.consumerId, + keys: [getMockKey(), getMockKey()], + }; + const clientsById = new Map([[client.id, client]]); + await addOneClient(client); + + // token-generation-states + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: client.keys[0].kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + PK: tokenGenStatesClientKidPurposePK, + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: client.keys[0].kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: client.keys[0].encodedPem, + GSIPK_clientId_purposeId: makeGSIPKClientIdPurposeId({ + clientId: client.id, + purposeId: purpose.id, + }), + GSIPK_purposeId: purpose.id, + purposeState: itemState.active, + purposeVersionId: purpose.versions[0].id, + agreementId: agreement.id, + agreementState: itemState.active, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }), + GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ + eserviceId: eservice.id, + descriptorId: eservice.descriptors[0].id, + }), + descriptorState: itemState.active, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + updatedAt: new Date().toISOString(), + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + it("should detect differences if the token-generation-states entry is incomplete", async () => { const descriptor: Descriptor = { ...getMockDescriptor(), @@ -1175,7 +1287,7 @@ describe("Token Generation Read Model Checker tests", () => { expect(clientDifferences).toEqual(expectedDifferencesLength); }); - it("should not detect differences if the purpose state in the token-generation-state entry is inactive and the purpose is missing in the read model", async () => { + it("should not detect differences if the purpose state in the token-generation-states is inactive and the purpose is missing in the read model", async () => { const descriptor: Descriptor = { ...getMockDescriptor(), state: descriptorState.published, @@ -1593,6 +1705,101 @@ describe("Token Generation Read Model Checker tests", () => { expect(clientDifferences).toEqual(expectedDifferencesLength); }); + it("should detect differences if the token-generation-states has entries but should have zero", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.published, + audience: ["pagopa.it"], + }; + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + const eservicesById = new Map([[eservice.id, eservice]]); + await addOneEService(eservice); + + const purpose: Purpose = { + ...getMockPurpose([getMockPurposeVersion(purposeVersionState.active)]), + eserviceId: eservice.id, + }; + const purposesById = new Map([[purpose.id, purpose]]); + await addOnePurpose(purpose); + + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.active, + consumerId: purpose.consumerId, + eserviceId: eservice.id, + descriptorId: descriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const agreementsByConsumerIdEserviceId = new Map([ + [ + makeGSIPKConsumerIdEServiceId({ + consumerId: purpose.consumerId, + eserviceId: eservice.id, + }), + [agreement], + ], + ]); + await addOneAgreement(agreement); + + const client: Client = { + ...getMockClient(), + purposes: [], + consumerId: purpose.consumerId, + keys: [], + }; + const clientsById = new Map([[client.id, client]]); + + await addOneClient(client); + + // token-generation-states + const mockKey = getMockKey(); + const tokenGenStatesClientKidPurposePK = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: client.id, + kid: mockKey.kid, + purposeId: purpose.id, + }); + const tokenGenStatesConsumerClient: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient( + tokenGenStatesClientKidPurposePK + ), + consumerId: client.consumerId, + GSIPK_clientId: client.id, + GSIPK_clientId_kid: makeGSIPKClientIdKid({ + clientId: client.id, + kid: mockKey.kid, + }), + clientKind: clientKindTokenGenStates.consumer, + publicKey: mockKey.encodedPem, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const clientDifferences = await compareReadModelClientsAndTokenGenStates({ + tokenGenStatesByClient: new Map([ + [client.id, [tokenGenStatesConsumerClient]], + ]), + clientsById, + purposesById, + eservicesById, + agreementsByConsumerIdEserviceId, + logger: genericLogger, + }); + expect(clientDifferences).toEqual(expectedDifferencesLength); + }); + it("should detect differences when the token-generation-states entries are missing if the client has keys", async () => { const descriptor: Descriptor = { ...getMockDescriptor(), From fb779cc2fb150800697978f9d3ab29596b33afd0 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:14:34 +0100 Subject: [PATCH 112/126] Fix GSISK_agreementTimestamp in token-generation-readmodel-checker (#1371) Co-authored-by: Roberto Taglioni --- .../src/consumerServiceV1.ts | 29 +++---- .../src/consumerServiceV2.ts | 10 +-- .../src/utils.ts | 54 ++++++++++++- .../test/utils.test.ts | 76 ++++++++++++++++++- .../src/consumerServiceV2.ts | 10 +-- .../src/utils/utils.ts | 11 ++- 6 files changed, 157 insertions(+), 33 deletions(-) diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 8623227220..4e7498eeaa 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -14,7 +14,7 @@ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { Logger } from "pagopa-interop-commons"; import { readAgreementEntry, - updateAgreementStateInPlatformStatesEntry, + updateAgreementStateInPlatformStatesEntryV1, agreementStateToItemState, updateAgreementStateOnTokenGenStates, writeAgreementEntry, @@ -65,7 +65,7 @@ export async function handleMessageV1( agreementState.rejected, () => { logger.info( - `Skipping processing of entry ${agreement.id}. Reason: state ${agreement.state}` + `Skipping processing of agreement ${agreement.id}. Reason: state ${agreement.state}` ); return Promise.resolve(); } @@ -86,6 +86,7 @@ export async function handleMessageV1( agreementState.missingCertifiedAttributes, agreementState.pending, agreementState.rejected, + // eslint-disable-next-line sonarjs/no-identical-functions () => { logger.info( `Skipping processing of agreement ${agreement.id}. Reason: state ${agreement.state}` @@ -140,17 +141,18 @@ const handleActivationOrSuspension = async ( if (existingAgreementEntry) { if (existingAgreementEntry.version > incomingVersion) { logger.info( - `Skipping processing of entry ${existingAgreementEntry}. Reason: a more recent entry already exists` + `Skipping processing of entry ${primaryKey}. Reason: a more recent entry already exists` ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntry( + await updateAgreementStateInPlatformStatesEntryV1({ dynamoDBClient, primaryKey, - agreementStateToItemState(agreement.state), - incomingVersion, - logger - ); + state: agreementStateToItemState(agreement.state), + timestamp: agreementTimestamp, + version: incomingVersion, + logger, + }); } } else { if (agreement.stamps.activation === undefined) { @@ -245,13 +247,14 @@ const handleUpgrade = async ( ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntry( + await updateAgreementStateInPlatformStatesEntryV1({ dynamoDBClient, primaryKey, - agreementStateToItemState(agreement.state), - msgVersion, - logger - ); + state: agreementStateToItemState(agreement.state), + timestamp: agreementTimestamp, + version: msgVersion, + logger, + }); } } else { if (agreement.stamps.upgrade === undefined) { diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index a447671095..caa12e767c 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -16,7 +16,7 @@ import { agreementStateToItemState, deleteAgreementEntry, readAgreementEntry, - updateAgreementStateInPlatformStatesEntry, + updateAgreementStateInPlatformStatesEntryV2, updateAgreementStateOnTokenGenStates, writeAgreementEntry, isLatestAgreement, @@ -46,11 +46,11 @@ export async function handleMessageV2( if (existingAgreementEntry) { if (existingAgreementEntry.version > msg.version) { logger.info( - `Skipping processing of entry ${existingAgreementEntry}. Reason: a more recent entry already exists` + `Skipping processing of entry ${primaryKey}. Reason: a more recent entry already exists` ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntry( + await updateAgreementStateInPlatformStatesEntryV2( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), @@ -111,7 +111,7 @@ export async function handleMessageV2( ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntry( + await updateAgreementStateInPlatformStatesEntryV2( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), @@ -175,7 +175,7 @@ export async function handleMessageV2( ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntry( + await updateAgreementStateInPlatformStatesEntryV2( dynamoDBClient, primaryKey, agreementStateToItemState(agreement.state), diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index 5d7fd7c7e1..42ec814e0d 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -123,7 +123,59 @@ export const deleteAgreementEntry = async ( logger.info(`Platform-states. Deleted agreement entry ${primaryKey}`); }; -export const updateAgreementStateInPlatformStatesEntry = async ( +// This function differs from its V2 implementation, because there could be some timestamps missing for V1 agreements +export const updateAgreementStateInPlatformStatesEntryV1 = async ({ + dynamoDBClient, + primaryKey, + state, + timestamp, + version, + logger, +}: { + dynamoDBClient: DynamoDBClient; + primaryKey: PlatformStatesAgreementPK; + state: ItemState; + timestamp: string; + version: number; + logger: Logger; +}): Promise => { + const input: UpdateItemInput = { + ConditionExpression: "attribute_exists(PK)", + Key: { + PK: { + S: primaryKey, + }, + }, + ExpressionAttributeValues: { + ":newState": { + S: state, + }, + ":newTimestamp": { + S: timestamp, + }, + ":newVersion": { + N: version.toString(), + }, + ":newUpdatedAt": { + S: new Date().toISOString(), + }, + }, + ExpressionAttributeNames: { + "#state": "state", + }, + UpdateExpression: + "SET #state = :newState, GSISK_agreementTimestamp = :newTimestamp, version = :newVersion, updatedAt = :newUpdatedAt", + TableName: config.tokenGenerationReadModelTableNamePlatform, + ReturnValues: "NONE", + }; + const command = new UpdateItemCommand(input); + await dynamoDBClient.send(command); + logger.info( + `Platform-states. Updated agreement state in entry ${primaryKey}` + ); +}; + +export const updateAgreementStateInPlatformStatesEntryV2 = async ( dynamoDBClient: DynamoDBClient, primaryKey: PlatformStatesAgreementPK, state: ItemState, diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index 0bc2870c6f..c48ea630ce 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -41,7 +41,7 @@ import { import { genericLogger } from "pagopa-interop-commons"; import { z } from "zod"; import { - updateAgreementStateInPlatformStatesEntry, + updateAgreementStateInPlatformStatesEntryV2, readAgreementEntry, writeAgreementEntry, deleteAgreementEntry, @@ -49,6 +49,7 @@ import { updateAgreementStateOnTokenGenStates, updateAgreementStateAndDescriptorInfoOnTokenGenStates, isLatestAgreement, + updateAgreementStateInPlatformStatesEntryV1, } from "../src/utils.js"; import { dynamoDBClient } from "./utils.js"; @@ -67,13 +68,80 @@ describe("utils", async () => { vi.useRealTimers(); }); - describe("updateAgreementStateInPlatformStatesEntry", async () => { + describe("updateAgreementStateInPlatformStatesEntryV1", async () => { it("should throw error if previous entry doesn't exist", async () => { const primaryKey = makePlatformStatesAgreementPK( generateId() ); + const timestamp = new Date().toISOString(); expect( - updateAgreementStateInPlatformStatesEntry( + updateAgreementStateInPlatformStatesEntryV1({ + dynamoDBClient, + primaryKey, + state: itemState.active, + timestamp, + version: 1, + logger: genericLogger, + }) + ).rejects.toThrowError(ConditionalCheckFailedException); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(agreementEntry).toBeUndefined(); + }); + + it("should update state if previous entry exists", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const currentDate = new Date(); + + const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry(primaryKey), + GSISK_agreementTimestamp: sixHoursAgo.toISOString(), + }; + expect( + await readAgreementEntry(primaryKey, dynamoDBClient) + ).toBeUndefined(); + await writeAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient, + genericLogger + ); + await updateAgreementStateInPlatformStatesEntryV1({ + dynamoDBClient, + primaryKey, + state: itemState.active, + timestamp: currentDate.toISOString(), + version: 2, + logger: genericLogger, + }); + + const result = await readAgreementEntry(primaryKey, dynamoDBClient); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + ...previousAgreementStateEntry, + state: itemState.active, + GSISK_agreementTimestamp: currentDate.toISOString(), + version: 2, + updatedAt: new Date().toISOString(), + }; + + expect(result).toEqual(expectedAgreementEntry); + }); + }); + + describe("updateAgreementStateInPlatformStatesEntryV2", async () => { + it("should throw error if previous entry doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK( + generateId() + ); + expect( + updateAgreementStateInPlatformStatesEntryV2( dynamoDBClient, primaryKey, itemState.active, @@ -102,7 +170,7 @@ describe("utils", async () => { dynamoDBClient, genericLogger ); - await updateAgreementStateInPlatformStatesEntry( + await updateAgreementStateInPlatformStatesEntryV2( dynamoDBClient, primaryKey, itemState.active, diff --git a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts index e906263559..34f3ce7b03 100644 --- a/packages/catalog-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/catalog-platformstate-writer/src/consumerServiceV2.ts @@ -109,12 +109,10 @@ export async function handleMessageV2( previousDescriptor.state !== descriptorState.archived ) { logger.info( - `Skipping processing of descriptor ${ - previousDescriptor?.id - } (the previous descriptor). Reason: ${ - !previousDescriptor - ? "entry doesn't exists" - : "state is not archived" + `Skipping processing of previous descriptor${ + previousDescriptor + ? ` ${previousDescriptor.id}. Reason: state ${previousDescriptor.state} is not archived` + : ". Reason: there is only one" }` ); return Promise.resolve(); diff --git a/packages/token-generation-readmodel-checker/src/utils/utils.ts b/packages/token-generation-readmodel-checker/src/utils/utils.ts index dc62d70ad6..dd8f3b2080 100644 --- a/packages/token-generation-readmodel-checker/src/utils/utils.ts +++ b/packages/token-generation-readmodel-checker/src/utils/utils.ts @@ -451,16 +451,14 @@ export async function compareReadModelAgreementsWithPlatformStates({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }), - GSISK_agreementTimestamp: - agreement.stamps.activation?.when.toISOString() || - agreement.createdAt.toISOString(), + GSISK_agreementTimestamp: extractAgreementTimestamp(agreement), agreementDescriptorId: agreement.descriptorId, }; const objectsDiff = diff( ComparisonPlatformStatesAgreementEntry.parse(platformStatesEntry), expectedPlatformStatesAgreementEntry, - { sort: true, excludeKeys: ["GSISK_agreementTimestamp"] } + { sort: true } ); if (objectsDiff) { differencesCount++; @@ -888,3 +886,8 @@ export const descriptorStateToItemState = (state: DescriptorState): ItemState => state === descriptorState.published || state === descriptorState.deprecated ? itemState.active : itemState.inactive; + +export const extractAgreementTimestamp = (agreement: Agreement): string => + agreement.stamps.upgrade?.when.toISOString() || + agreement.stamps.activation?.when.toISOString() || + agreement.createdAt.toISOString(); From 1c51602781f8fbd61fd6fa82f89fe12b16e2ee3d Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:48:12 +0100 Subject: [PATCH 113/126] Fix logic --- .../src/services/catalogService.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/catalog-process/src/services/catalogService.ts b/packages/catalog-process/src/services/catalogService.ts index 952670939e..00863dfd95 100644 --- a/packages/catalog-process/src/services/catalogService.ts +++ b/packages/catalog-process/src/services/catalogService.ts @@ -1822,16 +1822,16 @@ export function catalogServiceBuilder( readModelService ); - const hasValidDescriptor = eservice.data.descriptors.some( - (descriptor) => - descriptor.state !== descriptorState.draft && - descriptor.state !== descriptorState.waitingForApproval && - descriptor.state !== descriptorState.archived - ); - if (!hasValidDescriptor) { + if ( + eservice.data.descriptors.every( + (descriptor) => + descriptor.state === descriptorState.draft || + descriptor.state === descriptorState.archived + ) + ) { throw eserviceWithoutValidDescriptors(eserviceId); } - + if (name !== eservice.data.name) { const eserviceWithSameName = await readModelService.getEServiceByNameAndProducerId({ From e5a9f89e919e32daa25ae54f23450cafb97756bf Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:48:23 +0100 Subject: [PATCH 114/126] Uncomment test --- .../catalog-process/test/updateEServiceName.test.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/catalog-process/test/updateEServiceName.test.ts b/packages/catalog-process/test/updateEServiceName.test.ts index ce233e0af3..87f1bd1d97 100644 --- a/packages/catalog-process/test/updateEServiceName.test.ts +++ b/packages/catalog-process/test/updateEServiceName.test.ts @@ -1,6 +1,9 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ import { genericLogger } from "pagopa-interop-commons"; -import { decodeProtobufPayload } from "pagopa-interop-commons-test/index.js"; +import { + decodeProtobufPayload, + getMockDelegation, +} from "pagopa-interop-commons-test/index.js"; import { Descriptor, descriptorState, @@ -10,6 +13,8 @@ import { generateId, EServiceNameUpdatedV2, TenantId, + delegationKind, + delegationState, } from "pagopa-interop-models"; import { expect, describe, it } from "vitest"; import { @@ -25,6 +30,7 @@ import { getMockDocument, getMockDescriptor, getMockEService, + addOneDelegation, } from "./utils.js"; describe("update eService name on published eservice", () => { it("should write on event-store for the update of the eService name", async () => { @@ -66,7 +72,6 @@ describe("update eService name on published eservice", () => { expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); }); - /* it("should write on event-store for the update of the eService name (delegate)", async () => { const descriptor: Descriptor = { ...getMockDescriptor(descriptorState.published), @@ -112,7 +117,6 @@ describe("update eService name on published eservice", () => { expect(writtenPayload.eservice).toEqual(toEServiceV2(updatedEService)); expect(writtenPayload.eservice).toEqual(toEServiceV2(returnedEService)); }); - */ it("should throw eServiceNotFound if the eservice doesn't exist", async () => { const eservice = getMockEService(); expect( @@ -136,7 +140,6 @@ describe("update eService name on published eservice", () => { }) ).rejects.toThrowError(operationForbidden); }); - /* it("should throw operationForbidden if the given e-service has been delegated and the requester is not the delegate", async () => { const eservice = getMockEService(); const delegation = getMockDelegation({ @@ -155,7 +158,6 @@ describe("update eService name on published eservice", () => { }) ).rejects.toThrowError(operationForbidden); }); - */ it("should throw eserviceWithoutValidDescriptors if the eservice doesn't have any descriptors", async () => { const eservice = getMockEService(); await addOneEService(eservice); From d1ab5a6e7152005b08f334e70f81ca96256afa5f Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:48:28 +0100 Subject: [PATCH 115/126] Fix test title --- packages/catalog-process/test/updateEserviceDescription.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-process/test/updateEserviceDescription.test.ts b/packages/catalog-process/test/updateEserviceDescription.test.ts index 99906570e9..a12183a669 100644 --- a/packages/catalog-process/test/updateEserviceDescription.test.ts +++ b/packages/catalog-process/test/updateEserviceDescription.test.ts @@ -159,7 +159,7 @@ describe("update eService description", () => { ) ).rejects.toThrowError(operationForbidden); }); - it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + it("should throw operationForbidden if the given e-service has been delegated and the requester is not the delegate", async () => { const eservice = getMockEService(); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, From d984f01f3c8758f476b9a88734eb56e1337c12b1 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:49:15 +0100 Subject: [PATCH 116/126] Revert "Fix test title" This reverts commit d1ab5a6e7152005b08f334e70f81ca96256afa5f. --- packages/catalog-process/test/updateEserviceDescription.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-process/test/updateEserviceDescription.test.ts b/packages/catalog-process/test/updateEserviceDescription.test.ts index a12183a669..99906570e9 100644 --- a/packages/catalog-process/test/updateEserviceDescription.test.ts +++ b/packages/catalog-process/test/updateEserviceDescription.test.ts @@ -159,7 +159,7 @@ describe("update eService description", () => { ) ).rejects.toThrowError(operationForbidden); }); - it("should throw operationForbidden if the given e-service has been delegated and the requester is not the delegate", async () => { + it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { const eservice = getMockEService(); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, From 953e1e931236c66b299872b19a3f873cd4a6a5b5 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:50:28 +0100 Subject: [PATCH 117/126] Reapply "Fix test title" This reverts commit d984f01f3c8758f476b9a88734eb56e1337c12b1. --- packages/catalog-process/test/updateEserviceDescription.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/catalog-process/test/updateEserviceDescription.test.ts b/packages/catalog-process/test/updateEserviceDescription.test.ts index 99906570e9..a12183a669 100644 --- a/packages/catalog-process/test/updateEserviceDescription.test.ts +++ b/packages/catalog-process/test/updateEserviceDescription.test.ts @@ -159,7 +159,7 @@ describe("update eService description", () => { ) ).rejects.toThrowError(operationForbidden); }); - it("should throw operationForbidden if the requester if the given e-service has been delegated and caller is not the delegate", async () => { + it("should throw operationForbidden if the given e-service has been delegated and the requester is not the delegate", async () => { const eservice = getMockEService(); const delegation = getMockDelegation({ kind: delegationKind.delegatedProducer, From 3e4d03da2043760dac0ae83d65b600651e25786b Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:53:30 +0100 Subject: [PATCH 118/126] Fix outbound-models version --- packages/agreement-outbound-writer/package.json | 2 +- pnpm-lock.yaml | 13 ++----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/packages/agreement-outbound-writer/package.json b/packages/agreement-outbound-writer/package.json index 8c9c0ff690..b4a27bb681 100644 --- a/packages/agreement-outbound-writer/package.json +++ b/packages/agreement-outbound-writer/package.json @@ -29,7 +29,7 @@ "vitest": "1.6.0" }, "dependencies": { - "@pagopa/interop-outbound-models": "1.1.0", + "@pagopa/interop-outbound-models": "1.0.11g", "@protobuf-ts/runtime": "2.9.4", "connection-string": "4.4.0", "dotenv-flow": "4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f25e3c076..9e2d805153 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,8 +98,8 @@ importers: packages/agreement-outbound-writer: dependencies: '@pagopa/interop-outbound-models': - specifier: 1.1.0 - version: 1.1.0 + specifier: 1.0.11g + version: 1.0.11-g '@protobuf-ts/runtime': specifier: 2.9.4 version: 2.9.4 @@ -4082,9 +4082,6 @@ packages: '@pagopa/interop-outbound-models@1.0.11-g': resolution: {integrity: sha512-hKdSmbkALTw6lDo0tKtji1EcarRyII5AiD4ma8I6vhhx+iTnmlPKXgNTc4FOUFsr8boFwK100GFeL7hLpBp7ZQ==} - '@pagopa/interop-outbound-models@1.1.0': - resolution: {integrity: sha512-F4MKL5eLOAlpsisVFRxVTe2CpmMHiVN94DOms6f6H63YFc8x4gNUHCFElJUgSQDYJkDAz+oTwZHr0LGUqIL0MA==} - '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -9382,12 +9379,6 @@ snapshots: ts-pattern: 5.2.0 zod: 3.23.8 - '@pagopa/interop-outbound-models@1.1.0': - dependencies: - '@protobuf-ts/runtime': 2.9.4 - ts-pattern: 5.2.0 - zod: 3.23.8 - '@pkgjs/parseargs@0.11.0': optional: true From 60e28caa347ba2dd58b1a45ac37825fd855657d9 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 12:56:35 +0100 Subject: [PATCH 119/126] Fix event --- packages/models/src/eservice/eserviceEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/models/src/eservice/eserviceEvents.ts b/packages/models/src/eservice/eserviceEvents.ts index e618744ff5..ab63934552 100644 --- a/packages/models/src/eservice/eserviceEvents.ts +++ b/packages/models/src/eservice/eserviceEvents.ts @@ -396,7 +396,7 @@ export const EServiceEventV2 = z.discriminatedUnion("type", [ z.object({ event_version: z.literal(2), type: z.literal("EServiceNameUpdated"), - data: protobufDecoder(EServiceDescriptionUpdatedV2), + data: protobufDecoder(EServiceNameUpdatedV2), }), ]); export type EServiceEventV2 = z.infer; From d4879b72ba6258dbfde6c305a88f5d033cc24982 Mon Sep 17 00:00:00 2001 From: AsterITA Date: Thu, 16 Jan 2025 15:02:17 +0100 Subject: [PATCH 120/126] PIN-5909: update riskAnalysis document (#1347) --- .../src/services/agreementContractBuilder.ts | 7 +- .../test/upgradeAgreement.test.ts | 7 +- .../commons/src/pdf-generator/pdfGenerator.ts | 11 +- packages/models/src/purpose/purpose.ts | 38 ---- .../src/model/domain/models.ts | 45 +++++ .../documents/riskAnalysisTemplate.html | 26 ++- .../src/services/purposeService.ts | 28 ++- .../services/riskAnalysisDocumentBuilder.ts | 34 +--- .../test/activatePurposeVersion.test.ts | 163 +++++++++++++++ .../test/createPurposeVersion.test.ts | 190 +++++++++++++++++- packages/purpose-process/test/utils.ts | 2 +- 11 files changed, 459 insertions(+), 92 deletions(-) create mode 100644 packages/purpose-process/src/model/domain/models.ts diff --git a/packages/agreement-process/src/services/agreementContractBuilder.ts b/packages/agreement-process/src/services/agreementContractBuilder.ts index e60d3c461d..a2dfc7c72b 100644 --- a/packages/agreement-process/src/services/agreementContractBuilder.ts +++ b/packages/agreement-process/src/services/agreementContractBuilder.ts @@ -7,6 +7,7 @@ import { PDFGenerator, dateAtRomeZone, formatDateyyyyMMddHHmmss, + getIpaCode, timeAtRomeZone, } from "pagopa-interop-commons"; import { @@ -25,7 +26,6 @@ import { tenantAttributeType, AgreementDocument, Delegation, - PUBLIC_ADMINISTRATIONS_IDENTIFIER, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { getVerifiedAttributeExpirationDate } from "pagopa-interop-agreement-lifecycle"; @@ -145,11 +145,6 @@ const getPdfPayload = async ( producerDelegationData: DelegationData | undefined, readModelService: ReadModelService ): Promise => { - const getIpaCode = (tenant: Tenant): string | undefined => - tenant.externalId.origin === PUBLIC_ADMINISTRATIONS_IDENTIFIER - ? tenant.externalId.value - : undefined; - const today = new Date(); const { certified, declared, verified } = await getAttributesData( diff --git a/packages/agreement-process/test/upgradeAgreement.test.ts b/packages/agreement-process/test/upgradeAgreement.test.ts index 08bcc5569e..1bcab1aa8d 100644 --- a/packages/agreement-process/test/upgradeAgreement.test.ts +++ b/packages/agreement-process/test/upgradeAgreement.test.ts @@ -7,6 +7,7 @@ import { FileManagerError, formatDateyyyyMMddHHmmss, genericLogger, + getIpaCode, timeAtRomeZone, } from "pagopa-interop-commons"; import { @@ -36,7 +37,6 @@ import { Descriptor, EService, EServiceId, - PUBLIC_ADMINISTRATIONS_IDENTIFIER, Tenant, TenantId, agreementState, @@ -801,11 +801,6 @@ describe("upgrade Agreement", () => { expect(submitterId).toEqual(actualAgreementUpgraded.stamps.submission?.who); expect(activatorId).toEqual(actualAgreementUpgraded.stamps.activation?.who); - const getIpaCode = (tenant: Tenant): string | undefined => - tenant.externalId.origin === PUBLIC_ADMINISTRATIONS_IDENTIFIER - ? tenant.externalId.value - : undefined; - const expectedAgreementContractPDFPayload: AgreementContractPDFPayload = { todayDate: dateAtRomeZone(currentExecutionTime), todayTime: timeAtRomeZone(currentExecutionTime), diff --git a/packages/commons/src/pdf-generator/pdfGenerator.ts b/packages/commons/src/pdf-generator/pdfGenerator.ts index 1871542f0a..1d7331e7f0 100644 --- a/packages/commons/src/pdf-generator/pdfGenerator.ts +++ b/packages/commons/src/pdf-generator/pdfGenerator.ts @@ -1,7 +1,11 @@ /* eslint-disable functional/no-let */ import path from "path"; import { fileURLToPath } from "url"; -import { pdfGenerationError } from "pagopa-interop-models"; +import { + pdfGenerationError, + PUBLIC_ADMINISTRATIONS_IDENTIFIER, + Tenant, +} from "pagopa-interop-models"; import puppeteer, { Browser } from "puppeteer"; import { buildHTMLTemplateService } from "../index.js"; @@ -95,3 +99,8 @@ export async function initPDFGenerator(): Promise { }, }; } + +export const getIpaCode = (tenant: Tenant): string | undefined => + tenant.externalId.origin === PUBLIC_ADMINISTRATIONS_IDENTIFIER + ? tenant.externalId.value + : undefined; diff --git a/packages/models/src/purpose/purpose.ts b/packages/models/src/purpose/purpose.ts index 1a36620c92..7c9ef088e0 100644 --- a/packages/models/src/purpose/purpose.ts +++ b/packages/models/src/purpose/purpose.ts @@ -7,7 +7,6 @@ import { TenantId, } from "../brandedIds.js"; import { PurposeRiskAnalysisForm } from "../risk-analysis/riskAnalysis.js"; -import { EServiceMode } from "../eservice/eservice.js"; export const purposeVersionState = { draft: "Draft", @@ -60,40 +59,3 @@ 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; - -export const PurposeDocumentEServiceInfo = z.object({ - name: z.string(), - mode: EServiceMode, - producerName: z.string(), - producerOrigin: z.string(), - producerIPACode: z.string(), - consumerName: z.string(), - consumerOrigin: z.string(), - consumerIPACode: z.string(), -}); -export type PurposeDocumentEServiceInfo = z.infer< - typeof PurposeDocumentEServiceInfo ->; - -export type RiskAnalysisDocumentPDFPayload = { - dailyCalls: string; - answers: string; - eServiceName: string; - producerText: string; - consumerText: string; - freeOfCharge: string; - freeOfChargeReason: string; - date: string; - eServiceMode: string; -}; 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..79ed97c514 --- /dev/null +++ b/packages/purpose-process/src/model/domain/models.ts @@ -0,0 +1,45 @@ +import { DelegationId, EServiceMode } from "pagopa-interop-models"; +import { z } from "zod"; + +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; + +export const PurposeDocumentEServiceInfo = z.object({ + name: z.string(), + mode: EServiceMode, + producerName: z.string(), + producerIpaCode: z.string().optional(), + consumerName: z.string(), + consumerIpaCode: z.string().optional(), + producerDelegationId: DelegationId.optional(), + producerDelegateName: z.string().optional(), + producerDelegateIpaCode: z.string().optional(), +}); +export type PurposeDocumentEServiceInfo = z.infer< + typeof PurposeDocumentEServiceInfo +>; + +export type RiskAnalysisDocumentPDFPayload = { + dailyCalls: string; + answers: string; + eServiceName: string; + producerName: string; + producerIpaCode: string | undefined; + consumerName: string; + consumerIpaCode: string | undefined; + freeOfCharge: string; + freeOfChargeReason: string; + date: string; + eServiceMode: string; + producerDelegationId: DelegationId | undefined; + producerDelegateName: string | undefined; + producerDelegateIpaCode: string | undefined; +}; diff --git a/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html b/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html index 00cdb21709..50a86a486e 100644 --- a/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html +++ b/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html @@ -48,13 +48,31 @@

Analisi del rischio

-
Erogatore
-
{{producerText}}
+
Ente erogatore
+
+ {{producerName}}{{#if producerIPACode}} (codice IPA: + {{producerIPACode}}){{/if}} +
+ {{#if producerDelegationId}}
-
Fruitore
-
{{consumerText}}
+
Ente delegato all’erogazione
+
+ {{producerDelegateName}}{{#if producerDelegateIpaCode}} (codice IPA: + {{producerDelegateIpaCode}}){{/if}} +
+
+ {{/if}} + +
+ +
+
Ente fruitore
+
+ {{consumerName}}{{#if consumerIpaCode}} (codice IPA: + {{consumerIpaCode}}){{/if}} +
diff --git a/packages/purpose-process/src/services/purposeService.ts b/packages/purpose-process/src/services/purposeService.ts index a1f65b3e81..f87b557c75 100644 --- a/packages/purpose-process/src/services/purposeService.ts +++ b/packages/purpose-process/src/services/purposeService.ts @@ -9,6 +9,7 @@ import { eventRepository, formatDateddMMyyyyHHmmss, getFormRulesByVersion, + getIpaCode, getLatestVersionFormRules, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, @@ -22,12 +23,10 @@ import { Purpose, PurposeId, TenantKind, - Ownership, PurposeVersion, PurposeVersionDocument, PurposeVersionDocumentId, PurposeVersionId, - ownership, purposeEventToBinaryData, purposeVersionState, PurposeRiskAnalysisForm, @@ -37,7 +36,6 @@ import { unsafeBrandId, generateId, Agreement, - PurposeDocumentEServiceInfo, RiskAnalysisId, RiskAnalysis, CorrelationId, @@ -90,6 +88,11 @@ import { toCreateEventWaitingForApprovalPurposeVersionDeleted, } from "../model/domain/toEvent.js"; import { config } from "../config/config.js"; +import { + ownership, + Ownership, + PurposeDocumentEServiceInfo, +} from "../model/domain/models.js"; import { GetPurposesFilters, ReadModelService } from "./readModelService.js"; import { assertOrganizationIsAConsumer, @@ -1562,20 +1565,29 @@ async function generateRiskAnalysisDocument({ pdfGenerator: PDFGenerator; logger: Logger; }): Promise { - const [producer, consumer] = await Promise.all([ + const [producer, consumer, producerDelegation] = await Promise.all([ retrieveTenant(eservice.producerId, readModelService), retrieveTenant(purpose.consumerId, readModelService), + readModelService.getActiveDelegation( + eservice.id, + delegationKind.delegatedProducer + ), ]); + const producerDelegate = + producerDelegation && + (await retrieveTenant(producerDelegation.delegateId, readModelService)); + const eserviceInfo: PurposeDocumentEServiceInfo = { name: eservice.name, mode: eservice.mode, producerName: producer.name, - producerOrigin: producer.externalId.origin, - producerIPACode: producer.externalId.value, + producerIpaCode: getIpaCode(producer), consumerName: consumer.name, - consumerOrigin: consumer.externalId.origin, - consumerIPACode: consumer.externalId.value, + consumerIpaCode: getIpaCode(consumer), + producerDelegationId: producerDelegation?.id, + producerDelegateName: producerDelegate?.name, + producerDelegateIpaCode: producerDelegate?.externalId.value, }; function getTenantKind(tenant: Tenant): TenantKind { diff --git a/packages/purpose-process/src/services/riskAnalysisDocumentBuilder.ts b/packages/purpose-process/src/services/riskAnalysisDocumentBuilder.ts index 78ce936a5b..efd71220f0 100644 --- a/packages/purpose-process/src/services/riskAnalysisDocumentBuilder.ts +++ b/packages/purpose-process/src/services/riskAnalysisDocumentBuilder.ts @@ -17,14 +17,12 @@ import { unexpectedEmptyAnswerError, } from "pagopa-interop-commons"; import { - PurposeDocumentEServiceInfo, Purpose, PurposeVersionDocument, PurposeVersionDocumentId, TenantKind, generateId, PurposeRiskAnalysisForm, - RiskAnalysisDocumentPDFPayload, eserviceMode, RiskAnalysisSingleAnswer, RiskAnalysisMultiAnswer, @@ -35,6 +33,10 @@ import { riskAnalysisConfigVersionNotFound, } from "../model/domain/errors.js"; import { PurposeProcessConfig } from "../config/config.js"; +import { + PurposeDocumentEServiceInfo, + RiskAnalysisDocumentPDFPayload, +} from "../model/domain/models.js"; const YES = "Sì"; const NO = "No"; @@ -165,20 +167,17 @@ const getPdfPayload = ({ dailyCalls: dailyCalls.toString(), answers, eServiceName: eserviceInfo.name, - producerText: formatTenantDescription( - eserviceInfo.producerName, - eserviceInfo.producerOrigin, - eserviceInfo.producerIPACode - ), - consumerText: formatTenantDescription( - eserviceInfo.consumerName, - eserviceInfo.consumerOrigin, - eserviceInfo.consumerIPACode - ), + producerName: eserviceInfo.producerName, + producerIpaCode: eserviceInfo.producerIpaCode, + consumerName: eserviceInfo.consumerName, + consumerIpaCode: eserviceInfo.consumerIpaCode, freeOfCharge: freeOfChargeHtml, freeOfChargeReason: freeOfChargeReasonHtml, date: dateAtRomeZone(new Date()), eServiceMode, + producerDelegationId: eserviceInfo.producerDelegationId, + producerDelegateName: eserviceInfo.producerDelegateName, + producerDelegateIpaCode: eserviceInfo.producerDelegateIpaCode, }; }; @@ -339,14 +338,3 @@ function formatFreeOfCharge( freeOfChargeReasonHtml, }; } - -function formatTenantDescription( - tenantName: string, - tenantOrigin: string, - tenantIPACode: string -): string { - if (tenantOrigin === "IPA") { - return `${tenantName} (codice IPA: ${tenantIPACode})`; - } - return tenantName; -} diff --git a/packages/purpose-process/test/activatePurposeVersion.test.ts b/packages/purpose-process/test/activatePurposeVersion.test.ts index c24c7cd809..57b7e527bd 100644 --- a/packages/purpose-process/test/activatePurposeVersion.test.ts +++ b/packages/purpose-process/test/activatePurposeVersion.test.ts @@ -1,7 +1,10 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable functional/no-let */ /* eslint-disable sonarjs/no-identical-functions */ /* eslint-disable @typescript-eslint/no-floating-promises */ +import path from "path"; +import { fileURLToPath } from "url"; import { getMockPurposeVersion, getMockPurpose, @@ -40,6 +43,7 @@ import { import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { genericLogger, + getIpaCode, riskAnalysisFormToRiskAnalysisFormToValidate, validateRiskAnalysis, } from "pagopa-interop-commons"; @@ -54,11 +58,15 @@ import { tenantNotFound, agreementNotFound, } from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { RiskAnalysisDocumentPDFPayload } from "../src/model/domain/models.js"; import { addOneAgreement, addOneDelegation, addOneEService, addOneTenant, + fileManager, + pdfGenerator, postgresDB, purposeService, } from "./utils.js"; @@ -125,6 +133,8 @@ describe("activatePurposeVersion", () => { }); it("should write on event-store for the activation of a purpose version in the waiting for approval state", async () => { + vi.spyOn(pdfGenerator, "generate"); + await addOnePurpose(mockPurpose); await addOneEService(mockEService); await addOneAgreement(mockAgreement); @@ -165,6 +175,126 @@ describe("activatePurposeVersion", () => { payload: writtenEvent.data, }); + const expectedPdfPayload: RiskAnalysisDocumentPDFPayload = { + dailyCalls: mockPurposeVersion.dailyCalls.toString(), + answers: expect.any(String), + eServiceName: mockEService.name, + producerName: mockProducer.name, + producerIpaCode: getIpaCode(mockProducer), + consumerName: mockConsumer.name, + consumerIpaCode: getIpaCode(mockConsumer), + freeOfCharge: expect.any(String), + freeOfChargeReason: expect.any(String), + date: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + eServiceMode: "Eroga", + producerDelegationId: undefined, + producerDelegateName: undefined, + producerDelegateIpaCode: undefined, + }; + + expect(pdfGenerator.generate).toBeCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates/documents", + "riskAnalysisTemplate.html" + ), + expectedPdfPayload + ); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(purposeVersion.riskAnalysis!.path); + + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + + it("should write on event-store for the activation of a purpose version in the waiting for approval state (With producer delegation)", async () => { + vi.spyOn(pdfGenerator, "generate"); + + const delegate = getMockTenant(); + + const producerDelegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegatorId: mockProducer.id, + delegateId: delegate.id, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOneDelegation(producerDelegation); + await addOneTenant(delegate); + await addOnePurpose(mockPurpose); + await addOneEService(mockEService); + await addOneAgreement(mockAgreement); + await addOneTenant(mockConsumer); + await addOneTenant(mockProducer); + + const purposeVersion = await purposeService.activatePurposeVersion({ + purposeId: mockPurpose.id, + versionId: mockPurposeVersion.id, + organizationId: delegate.id, + correlationId: generateId(), + logger: genericLogger, + }); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "PurposeVersionActivated", + event_version: 2, + }); + + const expectedPurpose: Purpose = { + ...mockPurpose, + suspendedByConsumer: false, + suspendedByProducer: false, + versions: [purposeVersion], + updatedAt: new Date(), + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: PurposeVersionActivatedV2, + payload: writtenEvent.data, + }); + + const expectedPdfPayload: RiskAnalysisDocumentPDFPayload = { + dailyCalls: mockPurposeVersion.dailyCalls.toString(), + answers: expect.any(String), + eServiceName: mockEService.name, + producerName: mockProducer.name, + producerIpaCode: getIpaCode(mockProducer), + consumerName: mockConsumer.name, + consumerIpaCode: getIpaCode(mockConsumer), + freeOfCharge: expect.any(String), + freeOfChargeReason: expect.any(String), + date: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + eServiceMode: "Eroga", + producerDelegationId: producerDelegation.id, + producerDelegateName: delegate.name, + producerDelegateIpaCode: delegate.externalId.value, + }; + + expect(pdfGenerator.generate).toBeCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates/documents", + "riskAnalysisTemplate.html" + ), + expectedPdfPayload + ); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(purposeVersion.riskAnalysis!.path); + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); }); @@ -457,6 +587,8 @@ describe("activatePurposeVersion", () => { }); it("should write on event-store for the activation of a purpose version in draft", async () => { + vi.spyOn(pdfGenerator, "generate"); + const purposeVersionMock: PurposeVersion = { ...mockPurposeVersion, state: purposeVersionState.draft, @@ -480,6 +612,37 @@ describe("activatePurposeVersion", () => { logger: genericLogger, }); + const expectedPdfPayload: RiskAnalysisDocumentPDFPayload = { + dailyCalls: purposeVersionMock.dailyCalls.toString(), + answers: expect.any(String), + eServiceName: mockEService.name, + producerName: mockProducer.name, + producerIpaCode: getIpaCode(mockProducer), + consumerName: mockConsumer.name, + consumerIpaCode: getIpaCode(mockConsumer), + freeOfCharge: expect.any(String), + freeOfChargeReason: expect.any(String), + date: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + eServiceMode: "Eroga", + producerDelegationId: undefined, + producerDelegateName: undefined, + producerDelegateIpaCode: undefined, + }; + + expect(pdfGenerator.generate).toBeCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates/documents", + "riskAnalysisTemplate.html" + ), + expectedPdfPayload + ); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(purposeVersion.riskAnalysis!.path); + const writtenEvent = await readLastEventByStreamId( mockPurpose.id, "purpose", diff --git a/packages/purpose-process/test/createPurposeVersion.test.ts b/packages/purpose-process/test/createPurposeVersion.test.ts index 4594aabc46..48ca341bca 100644 --- a/packages/purpose-process/test/createPurposeVersion.test.ts +++ b/packages/purpose-process/test/createPurposeVersion.test.ts @@ -1,6 +1,9 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable sonarjs/no-identical-functions */ /* eslint-disable functional/no-let */ /* eslint-disable @typescript-eslint/no-floating-promises */ +import path from "path"; +import { fileURLToPath } from "url"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import { readLastEventByStreamId, @@ -13,6 +16,7 @@ import { getMockPurpose, getMockValidRiskAnalysisForm, writeInReadmodel, + getMockDelegation, } from "pagopa-interop-commons-test"; import { purposeVersionState, @@ -31,8 +35,10 @@ import { NewPurposeVersionActivatedV2, NewPurposeVersionWaitingForApprovalV2, toReadModelTenant, + delegationKind, + delegationState, } from "pagopa-interop-models"; -import { genericLogger } from "pagopa-interop-commons"; +import { genericLogger, getIpaCode } from "pagopa-interop-commons"; import { agreementNotFound, eserviceNotFound, @@ -43,9 +49,15 @@ import { tenantNotFound, unchangedDailyCalls, } from "../src/model/domain/errors.js"; +import { config } from "../src/config/config.js"; +import { RiskAnalysisDocumentPDFPayload } from "../src/model/domain/models.js"; import { + addOneDelegation, + addOneTenant, agreements, eservices, + fileManager, + pdfGenerator, postgresDB, purposeService, tenants, @@ -115,6 +127,8 @@ describe("createPurposeVersion", () => { }); it("should write on event-store for the creation of a new purpose version (daily calls increased and <= threshold)", async () => { + vi.spyOn(pdfGenerator, "generate"); + await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); @@ -131,6 +145,143 @@ describe("createPurposeVersion", () => { logger: genericLogger, }); + const expectedPdfPayload: RiskAnalysisDocumentPDFPayload = { + dailyCalls: "24", + answers: expect.any(String), + eServiceName: mockEService.name, + producerName: mockProducer.name, + producerIpaCode: getIpaCode(mockProducer), + consumerName: mockConsumer.name, + consumerIpaCode: getIpaCode(mockConsumer), + freeOfCharge: expect.any(String), + freeOfChargeReason: expect.any(String), + date: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + eServiceMode: "Eroga", + producerDelegationId: undefined, + producerDelegateName: undefined, + producerDelegateIpaCode: undefined, + }; + + expect(pdfGenerator.generate).toBeCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates/documents", + "riskAnalysisTemplate.html" + ), + expectedPdfPayload + ); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(returnedPurposeVersion.riskAnalysis!.path); + + const writtenEvent = await readLastEventByStreamId( + mockPurpose.id, + "purpose", + postgresDB + ); + + expect(writtenEvent).toMatchObject({ + stream_id: mockPurpose.id, + version: "1", + type: "NewPurposeVersionActivated", + event_version: 2, + }); + + const expectedPurposeVersion: PurposeVersion = { + id: returnedPurposeVersion.id, + createdAt: new Date(), + firstActivationAt: new Date(), + state: purposeVersionState.active, + dailyCalls: 24, + riskAnalysis: returnedPurposeVersion.riskAnalysis, + }; + + const expectedPurpose: Purpose = { + ...mockPurpose, + versions: [ + { + ...mockPurposeVersion, + state: purposeVersionState.archived, + updatedAt: new Date(), + }, + expectedPurposeVersion, + ], + updatedAt: new Date(), + }; + + const writtenPayload = decodeProtobufPayload({ + messageType: NewPurposeVersionActivatedV2, + payload: writtenEvent.data, + }); + + expect(returnedPurposeVersion).toEqual(expectedPurposeVersion); + expect(writtenPayload.purpose).toEqual(toPurposeV2(expectedPurpose)); + }); + + it("should write on event-store for the creation of a new purpose version (daily calls increased and <= threshold) (with producer delegation)", async () => { + vi.spyOn(pdfGenerator, "generate"); + + const delegate = getMockTenant(); + + const producerDelegation = getMockDelegation({ + kind: delegationKind.delegatedProducer, + delegatorId: mockProducer.id, + delegateId: delegate.id, + eserviceId: mockEService.id, + state: delegationState.active, + }); + + await addOnePurpose(mockPurpose); + await addOneDelegation(producerDelegation); + await addOneTenant(delegate); + await writeInReadmodel(toReadModelEService(mockEService), eservices); + await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); + await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); + await writeInReadmodel(toReadModelTenant(mockProducer), tenants); + + const returnedPurposeVersion = await purposeService.createPurposeVersion({ + purposeId: mockPurpose.id, + seed: { + dailyCalls: 24, + }, + organizationId: mockPurpose.consumerId, + correlationId: generateId(), + logger: genericLogger, + }); + + const expectedPdfPayload: RiskAnalysisDocumentPDFPayload = { + dailyCalls: "24", + answers: expect.any(String), + eServiceName: mockEService.name, + producerName: mockProducer.name, + producerIpaCode: getIpaCode(mockProducer), + consumerName: mockConsumer.name, + consumerIpaCode: getIpaCode(mockConsumer), + freeOfCharge: expect.any(String), + freeOfChargeReason: expect.any(String), + date: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + eServiceMode: "Eroga", + producerDelegationId: producerDelegation.id, + producerDelegateName: delegate.name, + producerDelegateIpaCode: delegate.externalId.value, + }; + + expect(pdfGenerator.generate).toBeCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates/documents", + "riskAnalysisTemplate.html" + ), + expectedPdfPayload + ); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(returnedPurposeVersion.riskAnalysis!.path); + const writtenEvent = await readLastEventByStreamId( mockPurpose.id, "purpose", @@ -176,6 +327,8 @@ describe("createPurposeVersion", () => { }); it("should write on event-store for the creation of a new purpose version (daily calls decreased and <= threshold)", async () => { + vi.spyOn(pdfGenerator, "generate"); + await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); @@ -192,6 +345,37 @@ describe("createPurposeVersion", () => { logger: genericLogger, }); + const expectedPdfPayload: RiskAnalysisDocumentPDFPayload = { + dailyCalls: "4", + answers: expect.any(String), + eServiceName: mockEService.name, + producerName: mockProducer.name, + producerIpaCode: getIpaCode(mockProducer), + consumerName: mockConsumer.name, + consumerIpaCode: getIpaCode(mockConsumer), + freeOfCharge: expect.any(String), + freeOfChargeReason: expect.any(String), + date: expect.stringMatching(/^\d{2}\/\d{2}\/\d{4}$/), + eServiceMode: "Eroga", + producerDelegationId: undefined, + producerDelegateName: undefined, + producerDelegateIpaCode: undefined, + }; + + expect(pdfGenerator.generate).toBeCalledWith( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../src", + "resources/templates/documents", + "riskAnalysisTemplate.html" + ), + expectedPdfPayload + ); + + expect( + await fileManager.listFiles(config.s3Bucket, genericLogger) + ).toContain(returnedPurposeVersion.riskAnalysis!.path); + const writtenEvent = await readLastEventByStreamId( mockPurpose.id, "purpose", @@ -342,7 +526,6 @@ describe("createPurposeVersion", () => { it("should throw eserviceNotFound if the e-service does not exists in the readmodel", async () => { await addOnePurpose(mockPurpose); - // await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); await writeInReadmodel(toReadModelTenant(mockProducer), tenants); @@ -386,7 +569,6 @@ describe("createPurposeVersion", () => { it("should throw agreementNotFound if the caller has no agreement associated with the purpose in the read model", async () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); - // await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); await writeInReadmodel(toReadModelTenant(mockProducer), tenants); @@ -443,7 +625,6 @@ describe("createPurposeVersion", () => { await addOnePurpose(mockPurpose); await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); - // await writeInReadmodel(mockConsumer, tenants); await writeInReadmodel(toReadModelTenant(mockProducer), tenants); expect(async () => { @@ -464,7 +645,6 @@ describe("createPurposeVersion", () => { await writeInReadmodel(toReadModelEService(mockEService), eservices); await writeInReadmodel(toReadModelAgreement(mockAgreement), agreements); await writeInReadmodel(toReadModelTenant(mockConsumer), tenants); - // await writeInReadmodel(mockProducer, tenants); expect(async () => { await purposeService.createPurposeVersion({ diff --git a/packages/purpose-process/test/utils.ts b/packages/purpose-process/test/utils.ts index 7d67e41b51..f1cb5ff436 100644 --- a/packages/purpose-process/test/utils.ts +++ b/packages/purpose-process/test/utils.ts @@ -67,7 +67,7 @@ afterAll(closeTestBrowserInstance); vi.spyOn(puppeteer, "launch").mockImplementation( async () => testBrowserInstance ); -const pdfGenerator = await initPDFGenerator(); +export const pdfGenerator = await initPDFGenerator(); export const purposeService = purposeServiceBuilder( postgresDB, From ff0b357f532e1c3181a33457f24c3c4864b87596 Mon Sep 17 00:00:00 2001 From: Roberto Taglioni Date: Thu, 16 Jan 2025 15:47:34 +0100 Subject: [PATCH 121/126] Remove duplicate event --- packages/authorization-updater/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/authorization-updater/src/index.ts b/packages/authorization-updater/src/index.ts index 5965fd5956..2fc79ee0ee 100644 --- a/packages/authorization-updater/src/index.ts +++ b/packages/authorization-updater/src/index.ts @@ -137,7 +137,6 @@ export async function sendCatalogAuthUpdate( "EServiceRiskAnalysisDeleted", "EServiceDescriptorAttributesUpdated", "EServiceDescriptionUpdated", - "EServiceDescriptorAttributesUpdated", "EServiceNameUpdated", "EServiceDescriptorSubmittedByDelegate", "EServiceDescriptorRejectedByDelegator" From c0778bbd6542fcc5c3b427b1742f2b95106ce924 Mon Sep 17 00:00:00 2001 From: Vittorio Caprio Date: Thu, 16 Jan 2025 16:47:56 +0100 Subject: [PATCH 122/126] PIN-4341 Update Agreement/Risk Analysis template (#1310) --- .../documents/agreementContractTemplate.css | 21 ++++++++- .../documents/agreementContractTemplate.html | 4 ++ .../templates/delegationApprovedTemplate.html | 4 ++ .../templates/delegationRevokedTemplate.html | 3 ++ .../templates/delegationTemplate.css | 19 ++++++++ .../documents/riskAnalysisTemplate.css | 43 ++++++++----------- .../documents/riskAnalysisTemplate.html | 4 +- 7 files changed, 70 insertions(+), 28 deletions(-) diff --git a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.css b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.css index 3ac437b2b7..4495b57eff 100644 --- a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.css +++ b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.css @@ -127,4 +127,23 @@ h2 { .content > div { margin-bottom: 8px; -} \ No newline at end of file +} + +.release-date { + margin: 32px 0; +} + +.release-date::after { + content: ""; + display: block; + clear: both; +} + +.release-date .date { + float: left; +} + +.release-date .date{ + display: inline-block; + width: 50%; +} diff --git a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html index ffcf79d2c8..bb50488e53 100644 --- a/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html +++ b/packages/agreement-process/src/resources/templates/documents/agreementContractTemplate.html @@ -147,5 +147,9 @@

Registrazione

fruitore quale soggetto titolato ad accedere all’e-service.
+ +
+
{{todayDate}}
+
diff --git a/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html index 4e84b7099d..802a1be4ab 100644 --- a/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html +++ b/packages/delegation-process/src/resources/templates/delegationApprovedTemplate.html @@ -84,5 +84,9 @@

Registrazione

per conto del delegante. + +
+
{{todayDate}}
+
diff --git a/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html index d87fe96aaf..1bbab5fb94 100644 --- a/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html +++ b/packages/delegation-process/src/resources/templates/delegationRevokedTemplate.html @@ -77,5 +77,8 @@

Registrazione

l’e-service per conto del delegante. +
+
{{todayDate}}
+
diff --git a/packages/delegation-process/src/resources/templates/delegationTemplate.css b/packages/delegation-process/src/resources/templates/delegationTemplate.css index 4e09620a97..91904ce070 100644 --- a/packages/delegation-process/src/resources/templates/delegationTemplate.css +++ b/packages/delegation-process/src/resources/templates/delegationTemplate.css @@ -128,3 +128,22 @@ h2 { .content > div { margin-bottom: 8px; } + +.release-date { + margin: 32px 0; +} + +.release-date::after { + content: ""; + display: block; + clear: both; +} + +.release-date .date { + float: left; +} + +.release-date .date { + display: inline-block; + width: 50%; +} diff --git a/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.css b/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.css index fd77d0ebf1..6c37225b02 100644 --- a/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.css +++ b/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.css @@ -125,30 +125,6 @@ h1 { font-weight: 600; } -.signature { - margin: 32px 0; -} - -.signature::after { - content: ""; - display: block; - clear: both; -} - -.signature .date { - float: left; -} - -.signature .date, -.signature .message { - display: inline-block; - width: 50%; -} - -.signature .message { - font-weight: 500; -} - .footer hr { border: 0; border-top: 1px solid #e3e7eb; @@ -182,3 +158,22 @@ h1 { font-size: 6px; line-height: 7.5px; } + +.release-date { + margin: 32px 0; +} + +.release-date::after { + content: ""; + display: block; + clear: both; +} + +.release-date .date { + float: left; +} + +.release-date .date { + display: inline-block; + width: 50%; +} diff --git a/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html b/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html index 50a86a486e..acd0d551b7 100644 --- a/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html +++ b/packages/purpose-process/src/resources/templates/documents/riskAnalysisTemplate.html @@ -94,10 +94,8 @@

Analisi del rischio

{{{answers}}}
- -
+
{{date}}
-
Firmato digitalmente da PagoPA S.p.A.
From 7cd811d2a517ac3725b5d9d6ddc9e2b996dd44e5 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Fri, 17 Jan 2025 09:50:22 +0100 Subject: [PATCH 123/126] Update platformstate-writers with agreement changes in platform-states (#1376) Co-authored-by: Roberto Taglioni --- .../schema/platform-states-dynamo-db.json | 30 - .../src/consumerServiceV1.ts | 184 +- .../src/consumerServiceV2.ts | 260 +-- .../src/utils.ts | 261 +-- .../consumerServiceV1.integration.test.ts | 1151 ++++++------- .../consumerServiceV2.integration.test.ts | 1528 +++++++---------- .../test/utils.test.ts | 316 ++-- .../src/consumerServiceV1.ts | 5 +- .../src/consumerServiceV2.ts | 5 +- .../src/utils.ts | 84 +- .../consumerServiceV1.integration.test.ts | 260 ++- .../consumerServiceV2.integration.test.ts | 333 ++-- .../test/utils.test.ts | 68 +- .../commons-test/src/setupDynamoDBtables.ts | 25 +- packages/commons-test/src/testUtils.ts | 12 +- .../src/tokenGenerationReadmodelUtils.ts | 8 +- packages/models/src/brandedIds.ts | 4 +- .../src/token-generation-readmodel/commons.ts | 17 +- .../platform-states-entry.ts | 19 +- .../purpose-platformstate-writer/src/utils.ts | 82 +- .../consumerServiceV1.integration.test.ts | 33 +- .../consumerServiceV2.integration.test.ts | 57 +- .../test/utils.test.ts | 118 +- .../test/utils.ts | 126 +- .../src/models/types.ts | 4 +- .../src/utils/utils.ts | 12 +- .../tokenGenerationReadModelChecker.test.ts | 74 +- .../test/utils.test.ts | 28 +- 28 files changed, 2162 insertions(+), 2942 deletions(-) diff --git a/docker/dynamo-db/schema/platform-states-dynamo-db.json b/docker/dynamo-db/schema/platform-states-dynamo-db.json index cf43152c90..686f540d42 100644 --- a/docker/dynamo-db/schema/platform-states-dynamo-db.json +++ b/docker/dynamo-db/schema/platform-states-dynamo-db.json @@ -4,14 +4,6 @@ { "AttributeName": "PK", "AttributeType": "S" - }, - { - "AttributeName": "GSIPK_consumerId_eserviceId", - "AttributeType": "S" - }, - { - "AttributeName": "GSISK_agreementTimestamp", - "AttributeType": "S" } ], "KeySchema": [ @@ -20,28 +12,6 @@ "KeyType": "HASH" } ], - "GlobalSecondaryIndexes": [ - { - "IndexName": "Agreement", - "KeySchema": [ - { - "AttributeName": "GSIPK_consumerId_eserviceId", - "KeyType": "HASH" - }, - { - "AttributeName": "GSISK_agreementTimestamp", - "KeyType": "RANGE" - } - ], - "Projection": { - "ProjectionType": "INCLUDE", - "NonKeyAttributes": [ - "state", - "agreementDescriptorId" - ] - } - } - ], "ProvisionedThroughput": { "ReadCapacityUnits": 10, "WriteCapacityUnits": 5 diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts index 4e7498eeaa..54d1a121de 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV1.ts @@ -5,23 +5,22 @@ import { AgreementV1, genericInternalError, fromAgreementV1, - makeGSIPKConsumerIdEServiceId, makePlatformStatesAgreementPK, PlatformStatesAgreementEntry, agreementState, + makeGSIPKConsumerIdEServiceId, } from "pagopa-interop-models"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { Logger } from "pagopa-interop-commons"; import { readAgreementEntry, - updateAgreementStateInPlatformStatesEntryV1, agreementStateToItemState, updateAgreementStateOnTokenGenStates, - writeAgreementEntry, deleteAgreementEntry, isLatestAgreement, updateLatestAgreementOnTokenGenStates, extractAgreementTimestamp, + upsertPlatformStatesAgreementEntry, } from "./utils.js"; export async function handleMessageV1( @@ -125,66 +124,68 @@ const handleActivationOrSuspension = async ( incomingVersion: number, logger: Logger ): Promise => { - const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const existingAgreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }); const agreementTimestamp = extractAgreementTimestamp(agreement); + if (agreement.stamps.activation === undefined) { + logger.warn( + `Missing agreement activation stamp for agreement with id ${agreement.id}. Using createdAt as fallback.` + ); + } - if (existingAgreementEntry) { - if (existingAgreementEntry.version > incomingVersion) { + if (isLatestAgreement(existingAgreementEntry, agreementTimestamp)) { + if ( + existingAgreementEntry && + existingAgreementEntry.version > incomingVersion && + existingAgreementEntry.agreementId === agreement.id + ) { logger.info( `Skipping processing of entry ${primaryKey}. Reason: a more recent entry already exists` ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntryV1({ - dynamoDBClient, - primaryKey, + const agreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, state: agreementStateToItemState(agreement.state), - timestamp: agreementTimestamp, version: incomingVersion, - logger, - }); - } - } else { - if (agreement.stamps.activation === undefined) { - logger.warn( - `Missing agreement activation stamp for agreement with id ${agreement.id}. Using createdAt as fallback.` + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp, + agreementDescriptorId: agreement.descriptorId, + }; + + await upsertPlatformStatesAgreementEntry( + agreementEntry, + dynamoDBClient, + logger ); } - const agreementEntry: PlatformStatesAgreementEntry = { - PK: primaryKey, - state: agreementStateToItemState(agreement.state), - version: incomingVersion, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: agreementTimestamp, - agreementDescriptorId: agreement.descriptorId, - }; - - await writeAgreementEntry(agreementEntry, dynamoDBClient, logger); + // token-generation-states + /* + In consumerServiceV2, the handler for activation and suspension doesn't have to update + the descriptor info in the token-generation-states. Here it's needed because this handler also + includes the agreement upgrade (which requires updating descriptor info). + */ + await updateLatestAgreementOnTokenGenStates( + dynamoDBClient, + agreement, + logger + ); + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry with agreementId ${agreement.id}. Reason: agreement is not the latest` + ); + return Promise.resolve(); } - - // token-generation-states - /* - In consumerServiceV2, the handler for activation and suspension doesn't have to update - the descriptor info in the token-generation-states. Here it's needed because this handler also - includes the agreement upgrade (which requires updating descriptor info). - */ - await updateLatestAgreementOnTokenGenStates( - dynamoDBClient, - agreement, - logger - ); }; const handleArchiving = async ( @@ -192,7 +193,12 @@ const handleArchiving = async ( dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { - const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, @@ -201,12 +207,8 @@ const handleArchiving = async ( const agreementTimestamp = extractAgreementTimestamp(agreement); if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) + isLatestAgreement(agreementEntry, agreementTimestamp) && + agreementEntry?.agreementId === agreement.id ) { // token-generation-states only if agreement is the latest await updateAgreementStateOnTokenGenStates({ @@ -215,13 +217,18 @@ const handleArchiving = async ( dynamoDBClient, logger, }); + + await deleteAgreementEntry( + primaryKey, + agreementEntry.agreementId, + dynamoDBClient, + logger + ); } else { logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + `Token-generation-states. Skipping processing of entry with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId} and agreement ${agreement.id}. Reason: agreement is not the latest` ); } - - await deleteAgreementEntry(primaryKey, dynamoDBClient, logger); }; const handleUpgrade = async ( @@ -230,55 +237,56 @@ const handleUpgrade = async ( msgVersion: number, logger: Logger ): Promise => { - const primaryKey = makePlatformStatesAgreementPK(agreement.id); - const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); - - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + const primaryKey = makePlatformStatesAgreementPK({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }); + const agreementEntry = await readAgreementEntry(primaryKey, dynamoDBClient); const agreementTimestamp = extractAgreementTimestamp(agreement); + if (agreement.stamps.upgrade === undefined) { + logger.warn( + `Missing agreement upgrade stamp for agreement with id ${agreement.id}. Using activation stamp or createdAt as fallback.` + ); + } - if (agreementEntry) { - if (agreementEntry.version > msgVersion) { + if (isLatestAgreement(agreementEntry, agreementTimestamp)) { + if ( + agreementEntry && + agreementEntry.version > msgVersion && + agreementEntry.agreementId === agreement.id + ) { logger.info( `Skipping processing of entry ${agreementEntry}. Reason: a more recent entry already exists` ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntryV1({ - dynamoDBClient, - primaryKey, + const newAgreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, state: agreementStateToItemState(agreement.state), - timestamp: agreementTimestamp, version: msgVersion, - logger, - }); - } - } else { - if (agreement.stamps.upgrade === undefined) { - logger.warn( - `Missing agreement upgrade stamp for agreement with id ${agreement.id}. Using activation stamp or createdAt as fallback.` + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp, + agreementDescriptorId: agreement.descriptorId, + }; + + await upsertPlatformStatesAgreementEntry( + newAgreementEntry, + dynamoDBClient, + logger ); } - const newAgreementEntry: PlatformStatesAgreementEntry = { - PK: primaryKey, - state: agreementStateToItemState(agreement.state), - version: msgVersion, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: agreementTimestamp, - agreementDescriptorId: agreement.descriptorId, - }; - - await writeAgreementEntry(newAgreementEntry, dynamoDBClient, logger); + await updateLatestAgreementOnTokenGenStates( + dynamoDBClient, + agreement, + logger + ); + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry with agreementId ${agreement.id}. Reason: agreement is not the latest` + ); + return Promise.resolve(); } - - await updateLatestAgreementOnTokenGenStates( - dynamoDBClient, - agreement, - logger - ); }; diff --git a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts index caa12e767c..cdc7ab67dc 100644 --- a/packages/agreement-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/agreement-platformstate-writer/src/consumerServiceV2.ts @@ -16,12 +16,12 @@ import { agreementStateToItemState, deleteAgreementEntry, readAgreementEntry, - updateAgreementStateInPlatformStatesEntryV2, + updateAgreementStateInPlatformStatesEntry, updateAgreementStateOnTokenGenStates, - writeAgreementEntry, isLatestAgreement, updateLatestAgreementOnTokenGenStates, extractAgreementTimestamp, + upsertPlatformStatesAgreementEntry, } from "./utils.js"; export async function handleMessageV2( @@ -32,57 +32,63 @@ export async function handleMessageV2( await match(message) .with({ type: "AgreementActivated" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); - const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const existingAgreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }); - if (existingAgreementEntry) { - if (existingAgreementEntry.version > msg.version) { + const agreementTimestamp = + agreement.stamps.activation?.when.toISOString(); + if (!agreementTimestamp) { + throw genericInternalError( + "An activated agreement should have activation stamp" + ); + } + + if (isLatestAgreement(existingAgreementEntry, agreementTimestamp)) { + if ( + existingAgreementEntry && + existingAgreementEntry.version > msg.version && + existingAgreementEntry.agreementId === agreement.id + ) { logger.info( `Skipping processing of entry ${primaryKey}. Reason: a more recent entry already exists` ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntryV2( + const agreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msg.version, + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp, + agreementDescriptorId: agreement.descriptorId, + }; + + await upsertPlatformStatesAgreementEntry( + agreementEntry, dynamoDBClient, - primaryKey, - agreementStateToItemState(agreement.state), - msg.version, logger ); - } - } else { - if (!agreement.stamps.activation) { - throw genericInternalError( - "An activated agreement should have activation stamp" + + await updateLatestAgreementOnTokenGenStates( + dynamoDBClient, + agreement, + logger ); } - const agreementEntry: PlatformStatesAgreementEntry = { - PK: primaryKey, - state: agreementStateToItemState(agreement.state), - version: msg.version, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - agreement.stamps.activation.when.toISOString(), - agreementDescriptorId: agreement.descriptorId, - }; - - await writeAgreementEntry(agreementEntry, dynamoDBClient, logger); + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry with agreementId ${agreement.id}. Reason: agreement is not the latest` + ); + return Promise.resolve(); } - - await updateLatestAgreementOnTokenGenStates( - dynamoDBClient, - agreement, - logger - ); }) .with( { type: "AgreementUnsuspendedByProducer" }, @@ -93,7 +99,10 @@ export async function handleMessageV2( { type: "AgreementSuspendedByPlatform" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); - const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const agreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient @@ -101,37 +110,34 @@ export async function handleMessageV2( const agreementTimestamp = extractAgreementTimestamp(agreement); - if (!agreementEntry || agreementEntry.version > msg.version) { - logger.info( - `Skipping processing of entry ${primaryKey}. Reason: ${ - !agreementEntry - ? "entry not found in platform-states" - : "a more recent entry already exists" - }` - ); - return Promise.resolve(); - } else { - await updateAgreementStateInPlatformStatesEntryV2( - dynamoDBClient, - primaryKey, - agreementStateToItemState(agreement.state), - msg.version, - logger - ); + if (isLatestAgreement(agreementEntry, agreementTimestamp)) { + if (!agreementEntry) { + logger.info( + `Skipping processing of entry ${primaryKey}. Reason: entry not found in platform-states` + ); + } else if ( + agreementEntry && + agreementEntry.version > msg.version && + agreementEntry.agreementId === agreement.id + ) { + logger.info( + `Skipping processing of entry ${primaryKey}. Reason: a more recent entry already exists` + ); + return Promise.resolve(); + } else { + await updateAgreementStateInPlatformStatesEntry( + dynamoDBClient, + primaryKey, + agreementStateToItemState(agreement.state), + msg.version, + logger + ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) - ) { // token-generation-states only if agreement is the latest await updateAgreementStateOnTokenGenStates({ GSIPK_consumerId_eserviceId, @@ -139,29 +145,45 @@ export async function handleMessageV2( dynamoDBClient, logger, }); - } else { - logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` - ); } + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry with agreementId ${ + agreement.id + }. Reason: ${ + agreementEntry + ? "entry not found in platform-states" + : "agreement is not the latest" + }` + ); + return Promise.resolve(); } } ) .with({ type: "AgreementUpgraded" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); - const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const agreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }); + const agreementTimestamp = agreement.stamps.upgrade?.when.toISOString(); + if (!agreementTimestamp) { + throw genericInternalError( + "An upgraded agreement should have an upgrade stamp" + ); + } - if (agreementEntry) { - if (agreementEntry.version > msg.version) { + if (isLatestAgreement(agreementEntry, agreementTimestamp)) { + if ( + agreementEntry && + agreementEntry.version > msg.version && + agreementEntry.agreementId === agreement.id + ) { logger.info( `Skipping processing of entry ${agreementEntry.PK}. Reason: a more recent entry already exists` ); @@ -171,52 +193,51 @@ export async function handleMessageV2( agreement.state !== agreementState.suspended ) { logger.info( - `Skipping processing of entry ${agreementEntry.PK}. Reason: the agreement state ${agreement.state} is not active or suspended` + `Skipping processing of entry ${primaryKey}. Reason: the agreement state ${agreement.state} is not active or suspended` ); return Promise.resolve(); } else { - await updateAgreementStateInPlatformStatesEntryV2( + const newAgreementEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: agreementStateToItemState(agreement.state), + version: msg.version, + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp, + agreementDescriptorId: agreement.descriptorId, + }; + + await upsertPlatformStatesAgreementEntry( + newAgreementEntry, dynamoDBClient, - primaryKey, - agreementStateToItemState(agreement.state), - msg.version, logger ); } - } else { - if (!agreement.stamps.upgrade) { - throw genericInternalError( - "An upgraded agreement should have an upgrade stamp" - ); - } - const newAgreementEntry: PlatformStatesAgreementEntry = { - PK: primaryKey, - state: agreementStateToItemState(agreement.state), - version: msg.version, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: agreement.stamps.upgrade.when.toISOString(), - agreementDescriptorId: agreement.descriptorId, - }; - await writeAgreementEntry(newAgreementEntry, dynamoDBClient, logger); + await updateLatestAgreementOnTokenGenStates( + dynamoDBClient, + agreement, + logger + ); + } else { + logger.info( + `Platform-states and token-generation-states. Skipping processing of entry with agreementId ${agreement.id}. Reason: agreement is not the latest` + ); + return Promise.resolve(); } - - await updateLatestAgreementOnTokenGenStates( - dynamoDBClient, - agreement, - logger - ); - }) - .with({ type: "AgreementArchivedByUpgrade" }, async (msg) => { - const agreement = parseAgreement(msg.data.agreement); - const pk = makePlatformStatesAgreementPK(agreement.id); - await deleteAgreementEntry(pk, dynamoDBClient, logger); }) .with({ type: "AgreementArchivedByConsumer" }, async (msg) => { const agreement = parseAgreement(msg.data.agreement); - const primaryKey = makePlatformStatesAgreementPK(agreement.id); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const agreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, @@ -225,12 +246,8 @@ export async function handleMessageV2( const agreementTimestamp = extractAgreementTimestamp(agreement); if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - agreementTimestamp, - dynamoDBClient - ) + isLatestAgreement(agreementEntry, agreementTimestamp) && + agreementEntry?.agreementId === agreement.id ) { // token-generation-states only if agreement is the latest await updateAgreementStateOnTokenGenStates({ @@ -239,13 +256,17 @@ export async function handleMessageV2( dynamoDBClient, logger, }); + await deleteAgreementEntry( + primaryKey, + agreementEntry.agreementId, + dynamoDBClient, + logger + ); } else { logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` + `Platform-states and Token-generation-states. Skipping processing of entry with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId} and agreement ${agreement.id}. Reason: agreement is not the latest` ); } - - await deleteAgreementEntry(primaryKey, dynamoDBClient, logger); }) .with( { type: "AgreementAdded" }, @@ -257,6 +278,7 @@ export async function handleMessageV2( { type: "AgreementConsumerDocumentRemoved" }, { type: "AgreementSetDraftByPlatform" }, { type: "AgreementSetMissingCertifiedAttributesByPlatform" }, + { type: "AgreementArchivedByUpgrade" }, () => Promise.resolve() ) .exhaustive(); diff --git a/packages/agreement-platformstate-writer/src/utils.ts b/packages/agreement-platformstate-writer/src/utils.ts index 42ec814e0d..3debc66773 100644 --- a/packages/agreement-platformstate-writer/src/utils.ts +++ b/packages/agreement-platformstate-writer/src/utils.ts @@ -11,15 +11,17 @@ import { PlatformStatesAgreementPK, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, - PlatformStatesAgreementGSIAgreement, TokenGenStatesConsumerClientGSIAgreement, Agreement, makePlatformStatesEServiceDescriptorPK, makeGSIPKEServiceIdDescriptorId, makeGSIPKConsumerIdEServiceId, + EServiceId, + TenantId, } from "pagopa-interop-models"; import { AttributeValue, + ConditionalCheckFailedException, DeleteItemCommand, DeleteItemInput, DynamoDBClient, @@ -39,13 +41,12 @@ import { z } from "zod"; import { Logger } from "pagopa-interop-commons"; import { config } from "./config/config.js"; -export const writeAgreementEntry = async ( +export const upsertPlatformStatesAgreementEntry = async ( agreementEntry: PlatformStatesAgreementEntry, dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { const input: PutItemInput = { - ConditionExpression: "attribute_not_exists(PK)", Item: { PK: { S: agreementEntry.PK, @@ -59,11 +60,11 @@ export const writeAgreementEntry = async ( updatedAt: { S: agreementEntry.updatedAt, }, - GSIPK_consumerId_eserviceId: { - S: agreementEntry.GSIPK_consumerId_eserviceId, + agreementId: { + S: agreementEntry.agreementId, }, - GSISK_agreementTimestamp: { - S: agreementEntry.GSISK_agreementTimestamp, + agreementTimestamp: { + S: agreementEntry.agreementTimestamp, }, agreementDescriptorId: { S: agreementEntry.agreementDescriptorId, @@ -73,7 +74,7 @@ export const writeAgreementEntry = async ( }; const command = new PutItemCommand(input); await dynamoDBClient.send(command); - logger.info(`Platform-states. Written agreement entry ${agreementEntry.PK}`); + logger.info(`Platform-states. Upserted agreement entry ${agreementEntry.PK}`); }; export const readAgreementEntry = async ( @@ -109,73 +110,41 @@ export const readAgreementEntry = async ( export const deleteAgreementEntry = async ( primaryKey: PlatformStatesAgreementPK, + agreementId: AgreementId, dynamoDBClient: DynamoDBClient, logger: Logger ): Promise => { - const input: DeleteItemInput = { - Key: { - PK: { S: primaryKey }, - }, - TableName: config.tokenGenerationReadModelTableNamePlatform, - }; - const command = new DeleteItemCommand(input); - await dynamoDBClient.send(command); - logger.info(`Platform-states. Deleted agreement entry ${primaryKey}`); -}; - -// This function differs from its V2 implementation, because there could be some timestamps missing for V1 agreements -export const updateAgreementStateInPlatformStatesEntryV1 = async ({ - dynamoDBClient, - primaryKey, - state, - timestamp, - version, - logger, -}: { - dynamoDBClient: DynamoDBClient; - primaryKey: PlatformStatesAgreementPK; - state: ItemState; - timestamp: string; - version: number; - logger: Logger; -}): Promise => { - const input: UpdateItemInput = { - ConditionExpression: "attribute_exists(PK)", - Key: { - PK: { - S: primaryKey, - }, - }, - ExpressionAttributeValues: { - ":newState": { - S: state, + try { + const input: DeleteItemInput = { + ConditionExpression: "#agreementId = :agreementId", + Key: { + PK: { S: primaryKey }, }, - ":newTimestamp": { - S: timestamp, + ExpressionAttributeNames: { + "#agreementId": "agreementId", }, - ":newVersion": { - N: version.toString(), - }, - ":newUpdatedAt": { - S: new Date().toISOString(), + ExpressionAttributeValues: { + ":agreementId": { + S: agreementId, + }, }, - }, - ExpressionAttributeNames: { - "#state": "state", - }, - UpdateExpression: - "SET #state = :newState, GSISK_agreementTimestamp = :newTimestamp, version = :newVersion, updatedAt = :newUpdatedAt", - TableName: config.tokenGenerationReadModelTableNamePlatform, - ReturnValues: "NONE", - }; - const command = new UpdateItemCommand(input); - await dynamoDBClient.send(command); - logger.info( - `Platform-states. Updated agreement state in entry ${primaryKey}` - ); + TableName: config.tokenGenerationReadModelTableNamePlatform, + }; + const command = new DeleteItemCommand(input); + await dynamoDBClient.send(command); + logger.info(`Platform-states. Deleted agreement entry ${primaryKey}`); + } catch (error: unknown) { + if (error instanceof ConditionalCheckFailedException) { + logger.info( + `Skipping deletion of agreement ${agreementId}. Reason: a more recent agreement exists in platform-states` + ); + } else { + throw error; + } + } }; -export const updateAgreementStateInPlatformStatesEntryV2 = async ( +export const updateAgreementStateInPlatformStatesEntry = async ( dynamoDBClient: DynamoDBClient, primaryKey: PlatformStatesAgreementPK, state: ItemState, @@ -345,71 +314,6 @@ export const updateAgreementStateAndDescriptorInfoOnTokenGenStatesEntries = } }; -export const readPlatformStateAgreementEntriesByConsumerIdEserviceId = async ( - consumerId_eserviceId: GSIPKConsumerIdEServiceId, - dynamoDBClient: DynamoDBClient -): Promise => { - const runPaginatedQuery = async ( - consumerId_eserviceId: GSIPKConsumerIdEServiceId, - dynamoDBClient: DynamoDBClient, - exclusiveStartKey?: Record - ): Promise => { - const input: QueryInput = { - TableName: config.tokenGenerationReadModelTableNamePlatform, - IndexName: "Agreement", - KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, - ExpressionAttributeValues: { - ":gsiValue": { S: consumerId_eserviceId }, - }, - ExclusiveStartKey: exclusiveStartKey, - ScanIndexForward: false, - }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); - - if (!data.Items) { - throw genericInternalError( - `Unable to read platform-states agreement entries: result ${JSON.stringify( - data - )} ` - ); - } else { - const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - - const agreementEntries = z - .array(PlatformStatesAgreementGSIAgreement) - .safeParse(unmarshalledItems); - - if (!agreementEntries.success) { - throw genericInternalError( - `Unable to parse platform-states agreement entries: result ${JSON.stringify( - agreementEntries - )} - data ${JSON.stringify(data)} ` - ); - } - - if (!data.LastEvaluatedKey) { - return agreementEntries.data; - } else { - return [ - ...agreementEntries.data, - ...(await runPaginatedQuery( - consumerId_eserviceId, - dynamoDBClient, - data.LastEvaluatedKey - )), - ]; - } - } - }; - - return await runPaginatedQuery( - consumerId_eserviceId, - dynamoDBClient, - undefined - ); -}; - export const updateAgreementStateAndDescriptorInfoOnTokenGenStates = async ({ GSIPK_consumerId_eserviceId, agreementId, @@ -623,35 +527,17 @@ export const readCatalogEntry = async ( } }; -/* -Because of DynamoDB eventual consistency, it's possible that the result of querying platform-states by -GSIPK_consumerId_eserviceId doesn't contain the entry with the latest agreement. -In this case, we need to check if the input agreement timestamp is more recent or equal than GSISK_agreementTimestamp -of the latest agreement in platform-states. -*/ -export const isLatestAgreement = async ( - GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId, - agreementId: AgreementId, - currentAgreementTimestamp: string, - dynamoDBClient: DynamoDBClient -): Promise => { - const agreementEntries = - await readPlatformStateAgreementEntriesByConsumerIdEserviceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); - - if (agreementEntries.length === 0) { +export const isLatestAgreement = ( + platformStatesAgreement: PlatformStatesAgreementEntry | undefined, + currentAgreementTimestamp: string +): boolean => { + if (!platformStatesAgreement) { return true; } - const agreementIdFromEntry = extractAgreementIdFromAgreementPK( - agreementEntries[0].PK - ); return ( - agreementIdFromEntry === agreementId || new Date(currentAgreementTimestamp) >= - new Date(agreementEntries[0].GSISK_agreementTimestamp) + new Date(platformStatesAgreement.agreementTimestamp) ); }; @@ -662,37 +548,26 @@ export const updateLatestAgreementOnTokenGenStates = async ( ): Promise => { const processAgreementUpdateOnTokenGenStates = async ( platformStatesCatalogEntry: PlatformStatesCatalogEntry | undefined, - GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId + consumerId: TenantId, + eserviceId: EServiceId ): Promise => { - const currentAgreementTimestamp = extractAgreementTimestamp(agreement); - if ( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreement.id, - currentAgreementTimestamp, - dynamoDBClient - ) - ) { - // token-generation-states only if agreement is the latest - const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - - await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ - GSIPK_consumerId_eserviceId, - agreementId: agreement.id, - agreementState: agreement.state, - dynamoDBClient, - GSIPK_eserviceId_descriptorId, - catalogEntry: platformStatesCatalogEntry, - logger, - }); - } else { - logger.info( - `Token-generation-states. Skipping processing of entry GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}. Reason: agreement is not the latest` - ); - } + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId: agreement.eserviceId, + descriptorId: agreement.descriptorId, + }); + + await updateAgreementStateAndDescriptorInfoOnTokenGenStates({ + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }), + agreementId: agreement.id, + agreementState: agreement.state, + dynamoDBClient, + GSIPK_eserviceId_descriptorId, + catalogEntry: platformStatesCatalogEntry, + logger, + }); }; const platformsStatesCatalogEntryPK = makePlatformStatesEServiceDescriptorPK({ @@ -703,14 +578,11 @@ export const updateLatestAgreementOnTokenGenStates = async ( platformsStatesCatalogEntryPK, dynamoDBClient ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }); await processAgreementUpdateOnTokenGenStates( platformStatesCatalogEntry, - GSIPK_consumerId_eserviceId + agreement.consumerId, + agreement.eserviceId ); // Second check @@ -727,11 +599,14 @@ export const updateLatestAgreementOnTokenGenStates = async ( ) { await processAgreementUpdateOnTokenGenStates( updatedPlatformStatesCatalogEntry, - GSIPK_consumerId_eserviceId + agreement.consumerId, + agreement.eserviceId ); } else { logger.info( - `Token-generation-states. Second retrieval of catalog entry ${platformsStatesCatalogEntryPK} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` + `Token-generation-states. Second retrieval of catalog entry ${platformsStatesCatalogEntryPK} didn't bring any updates to agreement with GSIPK_consumerId_eserviceId ${makeGSIPKConsumerIdEServiceId( + { consumerId: agreement.consumerId, eserviceId: agreement.eserviceId } + )}` ); } }; diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts index 2895ea9088..198c0101df 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -41,9 +41,10 @@ import { writePlatformCatalogEntry, writeTokenGenStatesConsumerClient, readAllTokenGenStatesItems, + writePlatformAgreementEntry, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; -import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; +import { readAgreementEntry } from "../src/utils.js"; import { handleMessageV1 } from "../src/consumerServiceV1.js"; import { dynamoDBClient } from "./utils.js"; @@ -87,18 +88,18 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + agreement.id + ), version: 2, }; - await writeAgreementEntry( - previousStateEntry, - dynamoDBClient, - genericLogger - ); + await writePlatformAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states const tokenGenStatesEntryPK1 = @@ -162,6 +163,7 @@ describe("integration tests V1 events", async () => { ]) ); }); + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { const agreement: Agreement = { ...getMockAgreement(), @@ -185,17 +187,21 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + agreement.id + ), + agreementDescriptorId: agreement.descriptorId, version: 2, }; - await writeAgreementEntry( - previousStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + previousPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -247,7 +253,7 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...previousStateEntry, + ...previousPlatformStatesAgreement, state: itemState.active, version: 3, updatedAt: new Date().toISOString(), @@ -287,6 +293,162 @@ describe("integration tests V1 events", async () => { ]) ); }); + + it("should update the entry if the agreement is the latest", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV1 = { + agreement: toAgreementV1(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementActivated", + event_version: 1, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); + const previousPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + previousAgreement.id + ), + agreementDescriptorId: previousAgreement.descriptorId, + agreementTimestamp: sixHoursAgo.toISOString(), + version: 8, + }; + await writePlatformAgreementEntry( + previousPlatformStatesAgreement, + dynamoDBClient + ); + + // token-generation-states + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + + await handleMessageV1(message, dynamoDBClient, genericLogger); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + state: itemState.active, + version: 1, + updatedAt: new Date().toISOString(), + agreementId: latestAgreement.id, + agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: latestAgreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_eserviceId_descriptorId, + updatedAt: new Date().toISOString(), + }; + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient2, + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_eserviceId_descriptorId, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, + ]) + ); + }); + it("should add the entry if it doesn't exist", async () => { const agreement: Agreement = { ...getMockAgreement(), @@ -310,9 +472,10 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); // token-generation-states const tokenGenStatesEntryPK1 = @@ -367,13 +530,9 @@ describe("integration tests V1 events", async () => { PK: agreementEntryPrimaryKey, version: 1, state: itemState.active, - updatedAt: agreement.stamps.activation!.when.toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); @@ -411,6 +570,7 @@ describe("integration tests V1 events", async () => { ]) ); }); + it("should add the entry if it doesn't exist - and add descriptor info to token-generation-states entry if missing", async () => { const agreement: Agreement = { ...getMockAgreement(), @@ -434,9 +594,10 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: agreement.eserviceId, @@ -511,13 +672,9 @@ describe("integration tests V1 events", async () => { PK: agreementEntryPrimaryKey, version: 1, state: itemState.active, - updatedAt: agreement.stamps.activation!.when.toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); @@ -561,7 +718,8 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should add the entry if it doesn't exist (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -605,47 +763,30 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: previousAgreement.eserviceId, - descriptorId: previousAgreement.descriptorId, + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, }); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + latestAgreement.id + ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); - // token-generation-states const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -656,11 +797,8 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementState: itemState.inactive, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: undefined, - descriptorAudience: undefined, - descriptorState: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient1, @@ -678,9 +816,6 @@ describe("integration tests V1 events", async () => { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: undefined, - descriptorAudience: undefined, - descriptorState: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient2, @@ -694,21 +829,7 @@ describe("integration tests V1 events", async () => { agreementEntryPrimaryKey, dynamoDBClient ); - - const expectedAgreementEntry: PlatformStatesAgreementEntry = { - PK: agreementEntryPrimaryKey, - version: 1, - state: itemState.active, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: previousAgreement.consumerId, - eserviceId: previousAgreement.eserviceId, - }), - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - agreementDescriptorId: previousAgreement.descriptorId, - }; - expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + expect(retrievedAgreementEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -725,11 +846,18 @@ describe("integration tests V1 events", async () => { }); describe("AgreementAdded (upgrade)", async () => { it("should do no operation if the table entry is more recent than incoming version", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, stamps: { activation: { + when: sixHoursAgo, + who: generateId(), + }, + upgrade: { when: new Date(), who: generateId(), }, @@ -747,33 +875,18 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), - version: 3, - }; - await writeAgreementEntry( - previousStateEntry, - dynamoDBClient, - genericLogger - ); - - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + agreement.id + ), + version: 3, }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states const tokenGenStatesEntryPK1 = @@ -838,11 +951,18 @@ describe("integration tests V1 events", async () => { }); it("should do no operation if the agreement state is not active nor suspended", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + const agreement: Agreement = { ...getMockAgreement(), state: agreementState.pending, stamps: { activation: { + when: sixHoursAgo, + who: generateId(), + }, + upgrade: { when: new Date(), who: generateId(), }, @@ -862,35 +982,19 @@ describe("integration tests V1 events", async () => { }; // platform-states - const platformStatesAgreementEntryPK = makePlatformStatesAgreementPK( - agreement.id - ); + const platformStatesAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(platformStatesAgreementEntryPK), + ...getMockPlatformStatesAgreementEntry( + platformStatesAgreementEntryPK, + agreement.id + ), version: 1, }; - await writeAgreementEntry( + await writePlatformAgreementEntry( platformStatesAgreementEntry, - dynamoDBClient, - genericLogger - ); - - const platformStatesCatalogEntryPK = - makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { - PK: platformStatesCatalogEntryPK, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry( - platformStatesCatalogEntry, dynamoDBClient ); @@ -956,7 +1060,7 @@ describe("integration tests V1 events", async () => { ); }); - it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { + it("should update the token generation read model if the incoming version is more recent than the platform-states entry and the agreement is the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -967,7 +1071,7 @@ describe("integration tests V1 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.active, + state: agreementState.suspended, stamps: { activation: { when: sixHoursAgo, @@ -981,7 +1085,8 @@ describe("integration tests V1 events", async () => { eserviceId, state: agreementState.active, stamps: { - activation: { + ...previousAgreement.stamps, + upgrade: { when: new Date(), who: generateId(), }, @@ -999,60 +1104,29 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + previousAgreement.id ), version: 1, state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: previousAgreement.stamps.activation!.when.toISOString(), }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - version: 1, - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), - }; - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: latestAgreement.eserviceId, - descriptorId: latestAgreement.descriptorId, - }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); - // token-generation-states const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -1095,14 +1169,17 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + PK: platformStatesAgreementPK, state: itemState.active, version: 2, updatedAt: new Date().toISOString(), + agreementId: latestAgreement.id, + agreementTimestamp: latestAgreement.stamps.upgrade!.when.toISOString(), + agreementDescriptorId: latestAgreement.descriptorId, }; expect(retrievedEntry).toEqual(expectedAgreementEntry); @@ -1140,7 +1217,8 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should update the entry if the incoming version is more recent than the table entry (agreement is not the latest -> no operation in token states)", async () => { + + it("should do no operation if the incoming agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1153,7 +1231,7 @@ describe("integration tests V1 events", async () => { eserviceId, state: agreementState.active, stamps: { - activation: { + upgrade: { when: sixHoursAgo, who: generateId(), }, @@ -1183,60 +1261,29 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), version: 1, - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - version: 1, - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + state: itemState.active, + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( + await writePlatformAgreementEntry( latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: previousAgreement.eserviceId, - descriptorId: previousAgreement.descriptorId, - }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); - // token-generation-states const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -1247,8 +1294,8 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, - agreementState: itemState.inactive, + agreementId: latestAgreement.id, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; await writeTokenGenStatesConsumerClient( @@ -1265,8 +1312,8 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, - agreementState: itemState.inactive, + agreementId: latestAgreement.id, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; await writeTokenGenStatesConsumerClient( @@ -1277,16 +1324,10 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.active, - version: 2, - updatedAt: new Date().toISOString(), - }; - expect(retrievedEntry).toEqual(expectedAgreementEntry); + expect(retrievedEntry).toEqual(latestAgreementStateEntry); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -1301,13 +1342,17 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should add the entry if it doesn't exist", async () => { + + it("should add the platform-states entry and update token-generation-states if the platform-states entry doesn't exist", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, stamps: { activation: { - when: new Date(), + when: sixHoursAgo, who: generateId(), }, upgrade: { @@ -1328,23 +1373,10 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states const tokenGenStatesEntryPK1 = @@ -1360,12 +1392,9 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementState: itemState.active, + agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: generateId(), - }), + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient1, @@ -1381,12 +1410,9 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementState: itemState.active, + agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: generateId(), - }), + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient2, @@ -1405,12 +1431,9 @@ describe("integration tests V1 events", async () => { PK: agreementEntryPrimaryKey, version: 1, state: itemState.active, - updatedAt: agreement.stamps.activation!.when.toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: agreement.stamps.upgrade!.when.toISOString(), + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.upgrade!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); @@ -1451,7 +1474,7 @@ describe("integration tests V1 events", async () => { }); describe("AgreementUpdated (suspended by producer)", async () => { - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1474,7 +1497,7 @@ describe("integration tests V1 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.suspended, + state: agreementState.active, stamps: { activation: { when: new Date(), @@ -1488,50 +1511,33 @@ describe("integration tests V1 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementUpdated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -1544,7 +1550,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1573,17 +1579,11 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.inactive, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -1598,26 +1598,12 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.suspended, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, @@ -1630,23 +1616,21 @@ describe("integration tests V1 events", async () => { }, }; const payload: AgreementUpdatedV1 = { - agreement: toAgreementV1(latestAgreement), + agreement: toAgreementV1(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 2, type: "AgreementUpdated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -1654,31 +1638,18 @@ describe("integration tests V1 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 1, }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + previousAgreementStateEntry, + dynamoDBClient ); // token-generation-states @@ -1691,13 +1662,9 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId, - descriptorId: latestAgreement.descriptorId, - }), }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient1, @@ -1713,13 +1680,9 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId, - descriptorId: latestAgreement.descriptorId, - }), }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient2, @@ -1729,13 +1692,14 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.inactive, updatedAt: new Date().toISOString(), + version: 2, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -1744,17 +1708,21 @@ describe("integration tests V1 events", async () => { const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: agreement.descriptorId, + }); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, - agreementId: latestAgreement.id, + GSIPK_eserviceId_descriptorId, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient2, - agreementId: latestAgreement.id, + GSIPK_eserviceId_descriptorId, agreementState: itemState.inactive, updatedAt: new Date().toISOString(), }; @@ -1793,9 +1761,10 @@ describe("integration tests V1 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); await handleMessageV1(message, dynamoDBClient, genericLogger); @@ -1807,12 +1776,8 @@ describe("integration tests V1 events", async () => { const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { PK: agreementEntryPrimaryKey, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), version: 1, updatedAt: new Date().toISOString(), agreementDescriptorId: agreement.descriptorId, @@ -1823,7 +1788,7 @@ describe("integration tests V1 events", async () => { }); describe("AgreementUpdated (unsuspended by producer)", async () => { - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1846,7 +1811,7 @@ describe("integration tests V1 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.active, + state: agreementState.suspended, stamps: { activation: { when: new Date(), @@ -1860,50 +1825,33 @@ describe("integration tests V1 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 3, type: "AgreementUpdated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -1916,7 +1864,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -1945,17 +1893,11 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.active, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -1970,26 +1912,12 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.active, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, @@ -2002,23 +1930,21 @@ describe("integration tests V1 events", async () => { }, }; const payload: AgreementUpdatedV1 = { - agreement: toAgreementV1(latestAgreement), + agreement: toAgreementV1(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 3, type: "AgreementUpdated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -2026,31 +1952,18 @@ describe("integration tests V1 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 2, }; - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); // token-generation-states @@ -2063,13 +1976,9 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, - agreementState: itemState.inactive, + agreementId: agreement.id, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId, - descriptorId: latestAgreement.descriptorId, - }), }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient1, @@ -2085,13 +1994,9 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, - agreementState: itemState.inactive, + agreementId: agreement.id, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId, - descriptorId: latestAgreement.descriptorId, - }), }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient2, @@ -2101,13 +2006,14 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.active, updatedAt: new Date().toISOString(), + version: 3, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -2116,17 +2022,21 @@ describe("integration tests V1 events", async () => { const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( dynamoDBClient ); + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: agreement.descriptorId, + }); const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient1, - agreementId: latestAgreement.id, + GSIPK_eserviceId_descriptorId, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = { ...tokenGenStatesConsumerClient2, - agreementId: latestAgreement.id, + GSIPK_eserviceId_descriptorId, agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -2142,88 +2052,59 @@ describe("integration tests V1 events", async () => { }); describe("Agreement Updated (archived by consumer or by upgrade)", () => { - it("agreement is the latest (includes operation on token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); - + it("should delete the platform-states entry and update token-generation-states if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.archived, + state: agreementState.active, stamps: { activation: { - when: sixHoursAgo, - who: generateId(), - }, - archiving: { when: new Date(), who: generateId(), }, }, }; - const latestAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, + const archivedAgreement: Agreement = { + ...agreement, state: agreementState.archived, - stamps: { - activation: { - when: new Date(), - who: generateId(), - }, - }, }; const payload: AgreementUpdatedV1 = { - agreement: toAgreementV1(latestAgreement), + agreement: toAgreementV1(archivedAgreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: archivedAgreement.id, + version: 2, type: "AgreementUpdated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + + // platform-states + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const platformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + state: itemState.active, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + platformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -2236,7 +2117,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2254,7 +2135,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2265,8 +2146,9 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); + // platform-states const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toBeUndefined(); @@ -2296,7 +2178,8 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("agreement is not the latest (no operation on token states)", async () => { + + it("should do no operation if the agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -2319,7 +2202,7 @@ describe("integration tests V1 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.archived, + state: agreementState.active, stamps: { activation: { when: new Date(), @@ -2333,47 +2216,34 @@ describe("integration tests V1 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementUpdated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + + // platform-states + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const platformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + state: itemState.active, + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + platformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -2386,7 +2256,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2404,7 +2274,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2415,11 +2285,12 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); + // platform-states const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toBeUndefined(); + expect(retrievedEntry).toEqual(platformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -2437,88 +2308,59 @@ describe("integration tests V1 events", async () => { }); describe("AgreementDeactivated (archived by consumer or by upgrade)", () => { - it("agreement is the latest (includes operation on token-generation-states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); - + it("should delete the platform-states entry and update token-generation-states if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.archived, + state: agreementState.active, stamps: { activation: { - when: sixHoursAgo, - who: generateId(), - }, - archiving: { when: new Date(), who: generateId(), }, }, }; - const latestAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, + const archivedAgreement: Agreement = { + ...agreement, state: agreementState.archived, - stamps: { - activation: { - when: new Date(), - who: generateId(), - }, - }, }; const payload: AgreementDeactivatedV1 = { - agreement: toAgreementV1(latestAgreement), + agreement: toAgreementV1(archivedAgreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: archivedAgreement.id, + version: 2, type: "AgreementDeactivated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + + // platform-states + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const platformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + state: itemState.active, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + platformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -2531,7 +2373,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2549,7 +2391,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2560,8 +2402,9 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); + // platform-states const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toBeUndefined(); @@ -2591,7 +2434,8 @@ describe("integration tests V1 events", async () => { ]) ); }); - it("agreement is not the latest (no operation on token-generation-states)", async () => { + + it("should do no operation if the agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -2614,7 +2458,7 @@ describe("integration tests V1 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.archived, + state: agreementState.active, stamps: { activation: { when: new Date(), @@ -2628,47 +2472,32 @@ describe("integration tests V1 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementDeactivated", event_version: 1, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const platformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + state: itemState.active, + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + platformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -2681,7 +2510,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2699,7 +2528,7 @@ describe("integration tests V1 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -2711,10 +2540,10 @@ describe("integration tests V1 events", async () => { await handleMessageV1(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toBeUndefined(); + expect(retrievedEntry).toEqual(platformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( diff --git a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts index 88e01adbdc..0be31795b6 100644 --- a/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/agreement-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -14,7 +14,6 @@ import { Agreement, AgreementActivatedV2, AgreementArchivedByConsumerV2, - AgreementArchivedByUpgradeV2, AgreementEventEnvelope, AgreementSuspendedByConsumerV2, AgreementSuspendedByPlatformV2, @@ -46,9 +45,10 @@ import { getMockPlatformStatesAgreementEntry, writePlatformCatalogEntry, readAllTokenGenStatesItems, + writePlatformAgreementEntry, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; -import { readAgreementEntry, writeAgreementEntry } from "../src/utils.js"; +import { readAgreementEntry } from "../src/utils.js"; import { handleMessageV2 } from "../src/consumerServiceV2.js"; import { dynamoDBClient } from "./utils.js"; @@ -69,7 +69,7 @@ describe("integration tests V2 events", async () => { }); describe("AgreementActivated", () => { - it("should do no operation if the existing table entry is more recent", async () => { + it("should do no operation if the existing platform-states entry is more recent", async () => { const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, @@ -92,17 +92,20 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + agreement.id + ), version: 2, }; - await writeAgreementEntry( - previousStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + previousPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -152,7 +155,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - expect(retrievedAgreementEntry).toEqual(previousStateEntry); + expect(retrievedAgreementEntry).toEqual(previousPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -167,7 +170,8 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry if the incoming version is more recent than existing table entry", async () => { + + it("should update the entry if the incoming version is more recent than the existing table entry", async () => { const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, @@ -190,17 +194,21 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const previousPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + agreement.id + ), + agreementDescriptorId: agreement.descriptorId, version: 2, }; - await writeAgreementEntry( - previousStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + previousPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -252,7 +260,7 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...previousStateEntry, + ...previousPlatformStatesAgreement, state: itemState.active, version: 3, updatedAt: new Date().toISOString(), @@ -292,6 +300,162 @@ describe("integration tests V2 events", async () => { ]) ); }); + + it("should update the entry if the agreement is the latest", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + + const consumerId = generateId(); + const eserviceId = generateId(); + const previousAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.archived, + stamps: { + activation: { + when: sixHoursAgo, + who: generateId(), + }, + }, + }; + + const latestAgreement: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId, + state: agreementState.active, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + const payload: AgreementActivatedV2 = { + agreement: toAgreementV2(latestAgreement), + }; + const message: AgreementEventEnvelope = { + sequence_num: 1, + stream_id: latestAgreement.id, + version: 1, + type: "AgreementActivated", + event_version: 2, + data: payload, + log_date: new Date(), + }; + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); + const previousPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + previousAgreement.id + ), + agreementDescriptorId: previousAgreement.descriptorId, + agreementTimestamp: sixHoursAgo.toISOString(), + version: 8, + }; + await writePlatformAgreementEntry( + previousPlatformStatesAgreement, + dynamoDBClient + ); + + // token-generation-states + const tokenGenStatesEntryPK1 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId, + }); + const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient1, + dynamoDBClient + ); + + const tokenGenStatesEntryPK2 = + makeTokenGenerationStatesClientKidPurposePK({ + clientId: generateId(), + kid: `kid ${Math.random()}`, + purposeId: generateId(), + }); + const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = + { + ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), + agreementState: itemState.inactive, + GSIPK_consumerId_eserviceId, + GSIPK_eserviceId_descriptorId: undefined, + }; + await writeTokenGenStatesConsumerClient( + tokenGenStatesConsumerClient2, + dynamoDBClient + ); + + await handleMessageV2(message, dynamoDBClient, genericLogger); + + // platform-states + const retrievedAgreementEntry = await readAgreementEntry( + agreementEntryPrimaryKey, + dynamoDBClient + ); + const expectedAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPrimaryKey, + state: itemState.active, + version: 1, + updatedAt: new Date().toISOString(), + agreementId: latestAgreement.id, + agreementTimestamp: + latestAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: latestAgreement.descriptorId, + }; + expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + + // token-generation-states + const GSIPK_eserviceId_descriptorId = makeGSIPKEServiceIdDescriptorId({ + eserviceId, + descriptorId: latestAgreement.descriptorId, + }); + const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( + dynamoDBClient + ); + const expectedTokenGenStatesConsumeClient1: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient1, + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_eserviceId_descriptorId, + updatedAt: new Date().toISOString(), + }; + const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = + { + ...tokenGenStatesConsumerClient2, + agreementId: latestAgreement.id, + agreementState: itemState.active, + GSIPK_eserviceId_descriptorId, + updatedAt: new Date().toISOString(), + }; + + expect(retrievedTokenGenStatesEntries).toHaveLength(2); + expect(retrievedTokenGenStatesEntries).toEqual( + expect.arrayContaining([ + expectedTokenGenStatesConsumeClient1, + expectedTokenGenStatesConsumeClient2, + ]) + ); + }); + it("should add the entry if it doesn't exist", async () => { const agreement: Agreement = { ...getMockAgreement(), @@ -315,9 +479,10 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); // token-generation-states const tokenGenStatesEntryPK1 = @@ -372,13 +537,9 @@ describe("integration tests V2 events", async () => { PK: agreementEntryPrimaryKey, version: 1, state: itemState.active, - updatedAt: agreement.stamps.activation!.when.toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); @@ -416,6 +577,7 @@ describe("integration tests V2 events", async () => { ]) ); }); + it("should add the entry if it doesn't exist - and add descriptor info to token-generation-states entry if missing", async () => { const agreement: Agreement = { ...getMockAgreement(), @@ -439,9 +601,10 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ eserviceId: agreement.eserviceId, @@ -516,13 +679,9 @@ describe("integration tests V2 events", async () => { PK: agreementEntryPrimaryKey, version: 1, state: itemState.active, - updatedAt: agreement.stamps.activation!.when.toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: - agreement.stamps.activation!.when.toISOString(), + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); @@ -567,7 +726,7 @@ describe("integration tests V2 events", async () => { ); }); - it("should add the entry if it doesn't exist (agreement is not the latest -> no operation on token states)", async () => { + it("should do no operation if the agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -611,47 +770,31 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: previousAgreement.eserviceId, - descriptorId: previousAgreement.descriptorId, + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, }); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + latestAgreement.id + ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); - // token-generation-states const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -662,11 +805,8 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementState: itemState.inactive, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: undefined, - descriptorAudience: undefined, - descriptorState: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient1, @@ -682,11 +822,8 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementState: itemState.inactive, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: undefined, - descriptorAudience: undefined, - descriptorState: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient2, @@ -700,21 +837,7 @@ describe("integration tests V2 events", async () => { agreementEntryPrimaryKey, dynamoDBClient ); - - const expectedAgreementEntry: PlatformStatesAgreementEntry = { - PK: agreementEntryPrimaryKey, - version: 1, - state: itemState.active, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: previousAgreement.consumerId, - eserviceId: previousAgreement.eserviceId, - }), - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - agreementDescriptorId: previousAgreement.descriptorId, - }; - expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); + expect(retrievedAgreementEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -729,6 +852,7 @@ describe("integration tests V2 events", async () => { ); }); }); + describe("AgreementSuspendedByProducer", async () => { it("should do no operation if the entry doesn't exist", async () => { const agreement: Agreement = { @@ -747,15 +871,16 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: agreement.id, - version: 1, + version: 2, type: "AgreementSuspendedByProducer", event_version: 2, data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -766,7 +891,8 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toBeUndefined(); }); - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -789,7 +915,7 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.suspended, + state: agreementState.active, stamps: { activation: { when: new Date(), @@ -803,50 +929,33 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementSuspendedByProducer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -859,7 +968,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -877,7 +986,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -888,17 +997,11 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.inactive, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -913,26 +1016,12 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.suspended, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, @@ -945,23 +1034,21 @@ describe("integration tests V2 events", async () => { }, }; const payload: AgreementSuspendedByProducerV2 = { - agreement: toAgreementV2(latestAgreement), + agreement: toAgreementV2(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 2, type: "AgreementSuspendedByProducer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -969,31 +1056,18 @@ describe("integration tests V2 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 1, }; - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); // token-generation-states @@ -1006,7 +1080,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1024,7 +1098,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1036,13 +1110,14 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.inactive, updatedAt: new Date().toISOString(), + version: 2, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -1073,6 +1148,7 @@ describe("integration tests V2 events", async () => { ); }); }); + describe("AgreementSuspendedByConsumer", async () => { it("should do no operation if the entry doesn't exist", async () => { const agreement: Agreement = { @@ -1091,15 +1167,16 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: agreement.id, - version: 1, + version: 2, type: "AgreementSuspendedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1110,7 +1187,8 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toBeUndefined(); }); - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1133,7 +1211,7 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.suspended, + state: agreementState.active, stamps: { activation: { when: new Date(), @@ -1147,50 +1225,33 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementSuspendedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -1203,7 +1264,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1221,7 +1282,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1232,17 +1293,11 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.inactive, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -1257,26 +1312,12 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.suspended, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, @@ -1289,23 +1330,21 @@ describe("integration tests V2 events", async () => { }, }; const payload: AgreementSuspendedByConsumerV2 = { - agreement: toAgreementV2(latestAgreement), + agreement: toAgreementV2(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 2, type: "AgreementSuspendedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -1313,31 +1352,16 @@ describe("integration tests V2 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + version: 1, }; - - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); // token-generation-states @@ -1350,7 +1374,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1368,7 +1392,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1380,13 +1404,14 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.inactive, updatedAt: new Date().toISOString(), + version: 2, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -1417,6 +1442,7 @@ describe("integration tests V2 events", async () => { ); }); }); + describe("AgreementSuspendedByPlatform", async () => { it("should do no operation if the entry doesn't exist", async () => { const agreement: Agreement = { @@ -1435,15 +1461,16 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: agreement.id, - version: 1, + version: 2, type: "AgreementSuspendedByPlatform", event_version: 2, data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1454,7 +1481,8 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toBeUndefined(); }); - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1491,50 +1519,33 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementSuspendedByPlatform", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -1547,7 +1558,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1565,7 +1576,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1576,17 +1587,11 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.inactive, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -1601,26 +1606,12 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.suspended, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, @@ -1633,23 +1624,21 @@ describe("integration tests V2 events", async () => { }, }; const payload: AgreementSuspendedByPlatformV2 = { - agreement: toAgreementV2(latestAgreement), + agreement: toAgreementV2(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 2, type: "AgreementSuspendedByPlatform", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -1657,31 +1646,16 @@ describe("integration tests V2 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + version: 1, }; - - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); // token-generation-states @@ -1694,7 +1668,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1712,7 +1686,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -1724,13 +1698,14 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.inactive, updatedAt: new Date().toISOString(), + version: 2, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -1761,6 +1736,7 @@ describe("integration tests V2 events", async () => { ); }); }); + describe("AgreementUnsuspendedByProducer", async () => { it("should do no operation if the entry doesn't exist", async () => { const agreement: Agreement = { @@ -1779,15 +1755,16 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: agreement.id, - version: 1, + version: 3, type: "AgreementUnsuspendedByProducer", event_version: 2, data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -1798,7 +1775,8 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toBeUndefined(); }); - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -1821,7 +1799,7 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.active, + state: agreementState.suspended, stamps: { activation: { when: new Date(), @@ -1835,50 +1813,34 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 3, type: "AgreementUnsuspendedByProducer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: latestAgreement.descriptorId, }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -1891,7 +1853,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -1909,7 +1871,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -1920,17 +1882,11 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.active, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -1945,55 +1901,39 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, state: agreementState.active, stamps: { activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.active, - stamps: { - activation: { - when: new Date(), + when: new Date(), who: generateId(), }, }, }; const payload: AgreementUnsuspendedByProducerV2 = { - agreement: toAgreementV2(latestAgreement), + agreement: toAgreementV2(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 3, type: "AgreementUnsuspendedByProducer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -2001,31 +1941,17 @@ describe("integration tests V2 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 2, }; - - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); // token-generation-states @@ -2038,7 +1964,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -2056,7 +1982,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -2068,13 +1994,14 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.active, updatedAt: new Date().toISOString(), + version: 3, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -2124,15 +2051,16 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: agreement.id, - version: 1, + version: 3, type: "AgreementUnsuspendedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); await handleMessageV2(message, dynamoDBClient, genericLogger); @@ -2143,7 +2071,8 @@ describe("integration tests V2 events", async () => { expect(retrievedAgreementEntry).toBeUndefined(); }); - it("should update the entry (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the message agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -2166,7 +2095,7 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.active, + state: agreementState.suspended, stamps: { activation: { when: new Date(), @@ -2180,50 +2109,33 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 3, type: "AgreementUnsuspendedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestPlatformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + latestPlatformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -2236,7 +2148,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -2254,7 +2166,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -2265,17 +2177,11 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); - const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.active, - updatedAt: new Date().toISOString(), - }; - const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toEqual(expectedAgreementStateEntry); + expect(retrievedEntry).toEqual(latestPlatformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -2290,26 +2196,12 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry (agreement is the latest -> update in token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + it("should update token generation read model if the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.active, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, - }, - }; - const latestAgreement: Agreement = { + const agreement: Agreement = { ...getMockAgreement(), consumerId, eserviceId, @@ -2322,23 +2214,21 @@ describe("integration tests V2 events", async () => { }, }; const payload: AgreementUnsuspendedByConsumerV2 = { - agreement: toAgreementV2(latestAgreement), + agreement: toAgreementV2(agreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: agreement.id, + version: 3, type: "AgreementUnsuspendedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, @@ -2346,31 +2236,16 @@ describe("integration tests V2 events", async () => { const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), + version: 2, }; - - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); // token-generation-states @@ -2383,7 +2258,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -2401,7 +2276,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, }; @@ -2413,13 +2288,14 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const expectedAgreementStateEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + ...previousAgreementStateEntry, state: itemState.active, updatedAt: new Date().toISOString(), + version: 3, }; const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toEqual(expectedAgreementStateEntry); @@ -2453,11 +2329,18 @@ describe("integration tests V2 events", async () => { describe("AgreementUpgraded", async () => { it("should do no operation if the table entry is more recent than incoming version", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, stamps: { activation: { + when: sixHoursAgo, + who: generateId(), + }, + upgrade: { when: new Date(), who: generateId(), }, @@ -2475,33 +2358,18 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const previousStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), - version: 3, - }; - await writeAgreementEntry( - previousStateEntry, - dynamoDBClient, - genericLogger - ); - - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), + const previousStateEntry: PlatformStatesAgreementEntry = { + ...getMockPlatformStatesAgreementEntry( + agreementEntryPrimaryKey, + agreement.id + ), + version: 3, }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousStateEntry, dynamoDBClient); // token-generation-states const tokenGenStatesEntryPK1 = @@ -2566,11 +2434,18 @@ describe("integration tests V2 events", async () => { }); it("should do no operation if the agreement state is not active nor suspended", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + const agreement: Agreement = { ...getMockAgreement(), state: agreementState.pending, stamps: { activation: { + when: sixHoursAgo, + who: generateId(), + }, + upgrade: { when: new Date(), who: generateId(), }, @@ -2590,35 +2465,19 @@ describe("integration tests V2 events", async () => { }; // platform-states - const platformStatesAgreementEntryPK = makePlatformStatesAgreementPK( - agreement.id - ); + const platformStatesAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(platformStatesAgreementEntryPK), + ...getMockPlatformStatesAgreementEntry( + platformStatesAgreementEntryPK, + agreement.id + ), version: 1, }; - await writeAgreementEntry( + await writePlatformAgreementEntry( platformStatesAgreementEntry, - dynamoDBClient, - genericLogger - ); - - const platformStatesCatalogEntryPK = - makePlatformStatesEServiceDescriptorPK({ - eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, - }); - const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { - PK: platformStatesCatalogEntryPK, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry( - platformStatesCatalogEntry, dynamoDBClient ); @@ -2684,7 +2543,7 @@ describe("integration tests V2 events", async () => { ); }); - it("should update the entry if the incoming version is more recent than the table entry (agreement is the latest -> update in token states)", async () => { + it("should update the token generation read model if the incoming version is more recent than the platform-states entry and the agreement is the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -2695,7 +2554,7 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.active, + state: agreementState.suspended, stamps: { activation: { when: sixHoursAgo, @@ -2709,7 +2568,7 @@ describe("integration tests V2 events", async () => { eserviceId, state: agreementState.active, stamps: { - activation: previousAgreement.stamps.activation, + ...previousAgreement.stamps, upgrade: { when: new Date(), who: generateId(), @@ -2728,60 +2587,29 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); const previousAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + previousAgreement.id ), - version: 1, + version: 8, state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + agreementTimestamp: previousAgreement.stamps.activation!.when.toISOString(), }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - version: 1, - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.upgrade!.when.toISOString(), - }; - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: latestAgreement.eserviceId, - descriptorId: latestAgreement.descriptorId, - }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); - // token-generation-states const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -2824,14 +2652,17 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...latestAgreementStateEntry, + PK: platformStatesAgreementPK, state: itemState.active, version: 2, updatedAt: new Date().toISOString(), + agreementId: latestAgreement.id, + agreementTimestamp: latestAgreement.stamps.upgrade!.when.toISOString(), + agreementDescriptorId: latestAgreement.descriptorId, }; expect(retrievedEntry).toEqual(expectedAgreementEntry); @@ -2869,7 +2700,8 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should update the entry if the incoming version is more recent than the table entry (agreement is not the latest -> no operation in token states)", async () => { + + it("should do no operation if the incoming agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -2882,7 +2714,7 @@ describe("integration tests V2 events", async () => { eserviceId, state: agreementState.active, stamps: { - activation: { + upgrade: { when: sixHoursAgo, who: generateId(), }, @@ -2912,60 +2744,29 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const latestAgreementStateEntry: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), version: 1, - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - version: 1, - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + state: itemState.active, + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( + await writePlatformAgreementEntry( latestAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ - eserviceId: previousAgreement.eserviceId, - descriptorId: previousAgreement.descriptorId, - }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); - // token-generation-states const tokenGenStatesEntryPK1 = makeTokenGenerationStatesClientKidPurposePK({ @@ -2976,8 +2777,8 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, - agreementState: itemState.inactive, + agreementId: latestAgreement.id, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; await writeTokenGenStatesConsumerClient( @@ -2994,8 +2795,8 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, - agreementState: itemState.inactive, + agreementId: latestAgreement.id, + agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; await writeTokenGenStatesConsumerClient( @@ -3006,16 +2807,10 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.active, - version: 2, - updatedAt: new Date().toISOString(), - }; - expect(retrievedEntry).toEqual(expectedAgreementEntry); + expect(retrievedEntry).toEqual(latestAgreementStateEntry); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( @@ -3030,13 +2825,17 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("add the entry if it doesn't exist", async () => { + + it("should add the platform-states entry and update token-generation-states if the platform-states entry doesn't exist", async () => { + const sixHoursAgo = new Date(); + sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); + const agreement: Agreement = { ...getMockAgreement(), state: agreementState.active, stamps: { activation: { - when: new Date(), + when: sixHoursAgo, who: generateId(), }, upgrade: { @@ -3057,23 +2856,10 @@ describe("integration tests V2 events", async () => { data: payload, log_date: new Date(), }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - const primaryKeyCatalogEntry = makePlatformStatesEServiceDescriptorPK({ + const agreementEntryPrimaryKey = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, - descriptorId: agreement.descriptorId, }); - const catalogEntry: PlatformStatesCatalogEntry = { - PK: primaryKeyCatalogEntry, - state: itemState.active, - descriptorAudience: ["pagopa.it"], - descriptorVoucherLifespan: 60, - version: 1, - updatedAt: new Date().toISOString(), - }; - - await writePlatformCatalogEntry(catalogEntry, dynamoDBClient); // token-generation-states const tokenGenStatesEntryPK1 = @@ -3089,12 +2875,9 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementState: itemState.active, + agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: generateId(), - }), + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient1, @@ -3110,12 +2893,9 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementState: itemState.active, + agreementState: itemState.inactive, GSIPK_consumerId_eserviceId, - GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: agreement.eserviceId, - descriptorId: generateId(), - }), + GSIPK_eserviceId_descriptorId: undefined, }; await writeTokenGenStatesConsumerClient( tokenGenStatesConsumerClient2, @@ -3134,12 +2914,9 @@ describe("integration tests V2 events", async () => { PK: agreementEntryPrimaryKey, version: 1, state: itemState.active, - updatedAt: agreement.stamps.activation!.when.toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: agreement.stamps.upgrade!.when.toISOString(), + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.upgrade!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; expect(retrievedAgreementEntry).toEqual(expectedAgreementEntry); @@ -3157,6 +2934,7 @@ describe("integration tests V2 events", async () => { ...tokenGenStatesConsumerClient1, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, + agreementState: itemState.active, updatedAt: new Date().toISOString(), }; const expectedTokenGenStatesConsumeClient2: TokenGenerationStatesConsumerClient = @@ -3164,6 +2942,7 @@ describe("integration tests V2 events", async () => { ...tokenGenStatesConsumerClient2, GSIPK_eserviceId_descriptorId, agreementId: agreement.id, + agreementState: itemState.active, updatedAt: new Date().toISOString(), }; @@ -3177,8 +2956,8 @@ describe("integration tests V2 events", async () => { }); }); - describe("AgreementArchivedByUpgrade", () => { - it("should delete the entry (no update in token-generation-states)", async () => { + describe("AgreementArchivedByConsumer", () => { + it("should delete the entry if it exists and the agreement is the latest", async () => { const consumerId = generateId(); const eserviceId = generateId(); @@ -3186,190 +2965,49 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.archived, + state: agreementState.active, stamps: { activation: { when: new Date(), who: generateId(), }, - archiving: { - when: new Date(), - who: generateId(), - }, - }, - }; - - const payload: AgreementArchivedByUpgradeV2 = { - agreement: toAgreementV2(agreement), - }; - const message: AgreementEventEnvelope = { - sequence_num: 1, - stream_id: agreement.id, - version: 1, - type: "AgreementArchivedByUpgrade", - event_version: 2, - data: payload, - log_date: new Date(), - }; - const agreementEntryPrimaryKey = makePlatformStatesAgreementPK( - agreement.id - ); - - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId, - eserviceId, - }); - const agreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementEntryPrimaryKey), - state: itemState.active, - GSIPK_consumerId_eserviceId, - }; - - await writeAgreementEntry( - agreementStateEntry, - dynamoDBClient, - genericLogger - ); - - // token-generation-states - const tokenGenStatesEntryPK1 = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = - { - ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: agreement.id, - agreementState: itemState.active, - GSIPK_consumerId_eserviceId, - }; - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient1, - dynamoDBClient - ); - - const tokenGenStatesEntryPK2 = - makeTokenGenerationStatesClientKidPurposePK({ - clientId: generateId(), - kid: `kid ${Math.random()}`, - purposeId: generateId(), - }); - const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = - { - ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: agreement.id, - agreementState: itemState.active, - GSIPK_consumerId_eserviceId, - }; - await writeTokenGenStatesConsumerClient( - tokenGenStatesConsumerClient2, - dynamoDBClient - ); - - await handleMessageV2(message, dynamoDBClient, genericLogger); - - const retrievedEntry = await readAgreementEntry( - agreementEntryPrimaryKey, - dynamoDBClient - ); - expect(retrievedEntry).toBeUndefined(); - - // token-generation-states - const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( - dynamoDBClient - ); - - expect(retrievedTokenGenStatesEntries).toHaveLength(2); - expect(retrievedTokenGenStatesEntries).toEqual( - expect.arrayContaining([ - tokenGenStatesConsumerClient1, - tokenGenStatesConsumerClient2, - ]) - ); - }); - }); - - describe("AgreementArchivedByConsumer", () => { - it("should delete the entry if it exists (agreement is the latest -> includes operation on token states)", async () => { - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); - - const consumerId = generateId(); - const eserviceId = generateId(); - - const previousAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, - state: agreementState.archived, - stamps: { - activation: { - when: sixHoursAgo, - who: generateId(), - }, }, }; - const latestAgreement: Agreement = { - ...getMockAgreement(), - consumerId, - eserviceId, + const archivedAgreement: Agreement = { + ...agreement, state: agreementState.archived, - stamps: { - activation: { - when: new Date(), - who: generateId(), - }, - }, }; const payload: AgreementArchivedByConsumerV2 = { - agreement: toAgreementV2(latestAgreement), + agreement: toAgreementV2(archivedAgreement), }; const message: AgreementEventEnvelope = { sequence_num: 1, - stream_id: latestAgreement.id, - version: 1, + stream_id: archivedAgreement.id, + version: 2, type: "AgreementArchivedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const platformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + agreement.id ), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - latestAgreement.stamps.activation!.when.toISOString(), + state: itemState.active, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + platformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -3382,7 +3020,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -3400,7 +3038,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: latestAgreement.id, + agreementId: agreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -3412,7 +3050,7 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - latestAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); expect(retrievedEntry).toBeUndefined(); @@ -3442,7 +3080,8 @@ describe("integration tests V2 events", async () => { ]) ); }); - it("should delete the entry (agreement is not the latest -> no operation on token states)", async () => { + + it("should do no operation if the agreement is not the latest", async () => { const sixHoursAgo = new Date(); sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); @@ -3465,7 +3104,7 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId, - state: agreementState.archived, + state: agreementState.active, stamps: { activation: { when: new Date(), @@ -3479,47 +3118,32 @@ describe("integration tests V2 events", async () => { const message: AgreementEventEnvelope = { sequence_num: 1, stream_id: previousAgreement.id, - version: 1, + version: 2, type: "AgreementArchivedByConsumer", event_version: 2, data: payload, log_date: new Date(), }; - const previousAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - previousAgreement.id - ); - const latestAgreementEntryPrimaryKey = makePlatformStatesAgreementPK( - latestAgreement.id - ); + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId, eserviceId, }); - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { + const platformStatesAgreement: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - previousAgreementEntryPrimaryKey + platformStatesAgreementPK, + latestAgreement.id ), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: - previousAgreement.stamps.activation!.when.toISOString(), - }; - const latestAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(latestAgreementEntryPrimaryKey), - state: itemState.inactive, - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: + state: itemState.active, + agreementTimestamp: latestAgreement.stamps.activation!.when.toISOString(), }; - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await writeAgreementEntry( - latestAgreementStateEntry, - dynamoDBClient, - genericLogger + await writePlatformAgreementEntry( + platformStatesAgreement, + dynamoDBClient ); // token-generation-states @@ -3532,7 +3156,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient1: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK1), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -3550,7 +3174,7 @@ describe("integration tests V2 events", async () => { const tokenGenStatesConsumerClient2: TokenGenerationStatesConsumerClient = { ...getMockTokenGenStatesConsumerClient(tokenGenStatesEntryPK2), - agreementId: previousAgreement.id, + agreementId: latestAgreement.id, agreementState: itemState.active, GSIPK_consumerId_eserviceId, }; @@ -3562,10 +3186,10 @@ describe("integration tests V2 events", async () => { await handleMessageV2(message, dynamoDBClient, genericLogger); const retrievedEntry = await readAgreementEntry( - previousAgreementEntryPrimaryKey, + platformStatesAgreementPK, dynamoDBClient ); - expect(retrievedEntry).toBeUndefined(); + expect(retrievedEntry).toEqual(platformStatesAgreement); // token-generation-states const retrievedTokenGenStatesEntries = await readAllTokenGenStatesItems( diff --git a/packages/agreement-platformstate-writer/test/utils.test.ts b/packages/agreement-platformstate-writer/test/utils.test.ts index c48ea630ce..dc42fe1725 100644 --- a/packages/agreement-platformstate-writer/test/utils.test.ts +++ b/packages/agreement-platformstate-writer/test/utils.test.ts @@ -8,6 +8,7 @@ import { getMockTokenGenStatesConsumerClient, readAllTokenGenStatesItems, readTokenGenStatesEntriesByGSIPKConsumerIdEServiceId, + writePlatformAgreementEntry, writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { @@ -41,15 +42,14 @@ import { import { genericLogger } from "pagopa-interop-commons"; import { z } from "zod"; import { - updateAgreementStateInPlatformStatesEntryV2, + updateAgreementStateInPlatformStatesEntry, readAgreementEntry, - writeAgreementEntry, deleteAgreementEntry, agreementStateToItemState, updateAgreementStateOnTokenGenStates, updateAgreementStateAndDescriptorInfoOnTokenGenStates, isLatestAgreement, - updateAgreementStateInPlatformStatesEntryV1, + upsertPlatformStatesAgreementEntry, } from "../src/utils.js"; import { dynamoDBClient } from "./utils.js"; @@ -68,80 +68,14 @@ describe("utils", async () => { vi.useRealTimers(); }); - describe("updateAgreementStateInPlatformStatesEntryV1", async () => { + describe("updateAgreementStateInPlatformStatesEntry", async () => { it("should throw error if previous entry doesn't exist", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); - const timestamp = new Date().toISOString(); - expect( - updateAgreementStateInPlatformStatesEntryV1({ - dynamoDBClient, - primaryKey, - state: itemState.active, - timestamp, - version: 1, - logger: genericLogger, - }) - ).rejects.toThrowError(ConditionalCheckFailedException); - const agreementEntry = await readAgreementEntry( - primaryKey, - dynamoDBClient - ); - expect(agreementEntry).toBeUndefined(); - }); - - it("should update state if previous entry exists", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); - - const sixHoursAgo = new Date(); - sixHoursAgo.setHours(sixHoursAgo.getHours() - 6); - - const currentDate = new Date(); - - const previousAgreementStateEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(primaryKey), - GSISK_agreementTimestamp: sixHoursAgo.toISOString(), - }; - expect( - await readAgreementEntry(primaryKey, dynamoDBClient) - ).toBeUndefined(); - await writeAgreementEntry( - previousAgreementStateEntry, - dynamoDBClient, - genericLogger - ); - await updateAgreementStateInPlatformStatesEntryV1({ - dynamoDBClient, - primaryKey, - state: itemState.active, - timestamp: currentDate.toISOString(), - version: 2, - logger: genericLogger, + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), }); - - const result = await readAgreementEntry(primaryKey, dynamoDBClient); - const expectedAgreementEntry: PlatformStatesAgreementEntry = { - ...previousAgreementStateEntry, - state: itemState.active, - GSISK_agreementTimestamp: currentDate.toISOString(), - version: 2, - updatedAt: new Date().toISOString(), - }; - - expect(result).toEqual(expectedAgreementEntry); - }); - }); - - describe("updateAgreementStateInPlatformStatesEntryV2", async () => { - it("should throw error if previous entry doesn't exist", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); expect( - updateAgreementStateInPlatformStatesEntryV2( + updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, itemState.active, @@ -157,20 +91,22 @@ describe("utils", async () => { }); it("should update state if previous entry exists", async () => { - const primaryKey = makePlatformStatesAgreementPK( + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const previousAgreementStateEntry = getMockPlatformStatesAgreementEntry( + primaryKey, generateId() ); - const previousAgreementStateEntry = - getMockPlatformStatesAgreementEntry(primaryKey); expect( await readAgreementEntry(primaryKey, dynamoDBClient) ).toBeUndefined(); - await writeAgreementEntry( + await writePlatformAgreementEntry( previousAgreementStateEntry, - dynamoDBClient, - genericLogger + dynamoDBClient ); - await updateAgreementStateInPlatformStatesEntryV2( + await updateAgreementStateInPlatformStatesEntry( dynamoDBClient, primaryKey, itemState.active, @@ -190,43 +126,60 @@ describe("utils", async () => { }); }); - describe("writeAgreementEntry", async () => { - it("should throw error if previous entry exists", async () => { - const primaryKey = makePlatformStatesAgreementPK( + describe("upsertPlatformStatesAgreementEntry", async () => { + it("should insert the entry if it doesn't exist", async () => { + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementStateEntry = getMockPlatformStatesAgreementEntry( + primaryKey, generateId() ); - const agreementStateEntry = - getMockPlatformStatesAgreementEntry(primaryKey); - await writeAgreementEntry( + expect( + await readAgreementEntry(primaryKey, dynamoDBClient) + ).toBeUndefined(); + + await upsertPlatformStatesAgreementEntry( agreementStateEntry, dynamoDBClient, genericLogger ); expect( - writeAgreementEntry(agreementStateEntry, dynamoDBClient, genericLogger) + writePlatformAgreementEntry(agreementStateEntry, dynamoDBClient) ).rejects.toThrowError(ConditionalCheckFailedException); }); - it("should write if previous entry doesn't exist", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), + it("should update the entry if it exists", async () => { + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), }); - const agreementStateEntry: PlatformStatesAgreementEntry = { + const previousPlatformStatesAgreement: PlatformStatesAgreementEntry = { PK: primaryKey, state: itemState.inactive, version: 1, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: generateId(), + agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }; + await writePlatformAgreementEntry( + previousPlatformStatesAgreement, + dynamoDBClient + ); - await writeAgreementEntry( - agreementStateEntry, + const updatedPlatformStatesAgreement: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.active, + version: 2, + updatedAt: new Date().toISOString(), + agreementId: generateId(), + agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + await upsertPlatformStatesAgreementEntry( + updatedPlatformStatesAgreement, dynamoDBClient, genericLogger ); @@ -235,16 +188,16 @@ describe("utils", async () => { primaryKey, dynamoDBClient ); - - expect(retrievedAgreementEntry).toEqual(agreementStateEntry); + expect(retrievedAgreementEntry).toEqual(updatedPlatformStatesAgreement); }); }); describe("readAgreementEntry", async () => { it("should return undefined if entry doesn't exist", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), + }); const agreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient @@ -253,27 +206,20 @@ describe("utils", async () => { }); it("should return entry if it exists", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), }); const agreementStateEntry: PlatformStatesAgreementEntry = { PK: primaryKey, state: itemState.inactive, version: 1, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: generateId(), + agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }; - await writeAgreementEntry( - agreementStateEntry, - dynamoDBClient, - genericLogger - ); + await writePlatformAgreementEntry(agreementStateEntry, dynamoDBClient); const retrievedEntry = await readAgreementEntry( primaryKey, dynamoDBClient @@ -285,43 +231,85 @@ describe("utils", async () => { describe("deleteAgreementEntry", async () => { it("should not throw error if previous entry doesn't exist", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); + const agreementId = generateId(); + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), + }); expect( - deleteAgreementEntry(primaryKey, dynamoDBClient, genericLogger) + deleteAgreementEntry( + primaryKey, + agreementId, + dynamoDBClient, + genericLogger + ) ).resolves.not.toThrowError(); }); it("should delete the entry if it exists", async () => { - const primaryKey = makePlatformStatesAgreementPK( - generateId() - ); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), }); - const agreementStateEntry: PlatformStatesAgreementEntry = { + const agreementPlatformStateEntry: PlatformStatesAgreementEntry = { PK: primaryKey, state: itemState.inactive, version: 1, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: generateId(), + agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }; - await writeAgreementEntry( - agreementStateEntry, + await writePlatformAgreementEntry( + agreementPlatformStateEntry, + dynamoDBClient + ); + await deleteAgreementEntry( + primaryKey, + agreementPlatformStateEntry.agreementId, dynamoDBClient, genericLogger ); - await deleteAgreementEntry(primaryKey, dynamoDBClient, genericLogger); const retrievedAgreementEntry = await readAgreementEntry( primaryKey, dynamoDBClient ); expect(retrievedAgreementEntry).toBeUndefined(); }); + + it("should not delete the entry if it refers to a different agreementId", async () => { + const agreementId1 = generateId(); + const agreementId2 = generateId(); + + const primaryKey = makePlatformStatesAgreementPK({ + consumerId: generateId(), + eserviceId: generateId(), + }); + const agreementPlatformStateEntry: PlatformStatesAgreementEntry = { + PK: primaryKey, + state: itemState.inactive, + version: 1, + updatedAt: new Date().toISOString(), + agreementId: agreementId1, + agreementTimestamp: new Date().toISOString(), + agreementDescriptorId: generateId(), + }; + await writePlatformAgreementEntry( + agreementPlatformStateEntry, + dynamoDBClient + ); + await deleteAgreementEntry( + primaryKey, + agreementId2, + dynamoDBClient, + genericLogger + ); + const retrievedAgreementEntry = await readAgreementEntry( + primaryKey, + dynamoDBClient + ); + expect(retrievedAgreementEntry).toEqual(agreementPlatformStateEntry); + }); }); describe("agreementStateToItemState", async () => { @@ -731,73 +719,45 @@ describe("utils", async () => { const threeHoursAgo = new Date(); threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ + const platformStatesAgreementPK = makePlatformStatesAgreementPK({ consumerId, eserviceId, }); - const agreementPK1 = makePlatformStatesAgreementPK(agreementId1); - const agreementPK2 = makePlatformStatesAgreementPK(agreementId2); - - const agreementEntry1: PlatformStatesAgreementEntry = { + const platformStatesAgreement1: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - agreementPK1, - GSIPK_consumerId_eserviceId + platformStatesAgreementPK, + agreementId1 ), - GSISK_agreementTimestamp: now.toISOString(), - state: itemState.active, + agreementTimestamp: now.toISOString(), }; - const agreementEntry2: PlatformStatesAgreementEntry = { + const platformStatesAgreement2: PlatformStatesAgreementEntry = { ...getMockPlatformStatesAgreementEntry( - agreementPK2, - GSIPK_consumerId_eserviceId + platformStatesAgreementPK, + agreementId2 ), - GSISK_agreementTimestamp: threeHoursAgo.toISOString(), - state: itemState.inactive, + agreementTimestamp: threeHoursAgo.toISOString(), }; - await writeAgreementEntry(agreementEntry1, dynamoDBClient, genericLogger); - await writeAgreementEntry(agreementEntry2, dynamoDBClient, genericLogger); - expect( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreementId1, - agreementEntry1.GSISK_agreementTimestamp, - dynamoDBClient + isLatestAgreement( + platformStatesAgreement2, + platformStatesAgreement1.agreementTimestamp ) ).toEqual(true); expect( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreementId2, - agreementEntry2.GSISK_agreementTimestamp, - dynamoDBClient + isLatestAgreement( + platformStatesAgreement1, + platformStatesAgreement2.agreementTimestamp ) ).toEqual(false); }); it("should return true if there are no other agreements", async () => { - const eserviceId = generateId(); - const consumerId = generateId(); - const agreementId1 = generateId(); const agreementTimestamp = new Date().toISOString(); - - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId, - eserviceId, - }); - - expect( - await isLatestAgreement( - GSIPK_consumerId_eserviceId, - agreementId1, - agreementTimestamp, - dynamoDBClient - ) - ).toEqual(true); + expect(isLatestAgreement(undefined, agreementTimestamp)).toEqual(true); }); }); }); diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts index c6e4476930..67596dbe8d 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV1.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV1.ts @@ -36,7 +36,6 @@ import { deleteEntriesFromTokenGenStatesByClientIdKidV1, deleteEntriesFromTokenGenStatesByClientIdV1, deleteEntriesFromTokenGenStatesByClientIdPurposeIdV1, - extractAgreementIdFromAgreementPK, extractKidFromTokenGenStatesEntryPK, readConsumerClientsInTokenGenStatesV1, readPlatformClientEntry, @@ -171,9 +170,7 @@ export async function handleMessageV1( : {}), ...(purposeEntry && agreementEntry ? { - agreementId: extractAgreementIdFromAgreementPK( - agreementEntry.PK - ), + agreementId: agreementEntry.agreementId, agreementState: agreementEntry.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ diff --git a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts index a857e1dee5..a64ee6dd96 100644 --- a/packages/authorization-platformstate-writer/src/consumerServiceV2.ts +++ b/packages/authorization-platformstate-writer/src/consumerServiceV2.ts @@ -28,7 +28,6 @@ import { deleteClientEntryFromPlatformStates, readPlatformClientEntry, deleteClientEntryFromTokenGenerationStates, - extractAgreementIdFromAgreementPK, retrievePlatformStatesByPurpose, upsertPlatformClientEntry, upsertTokenGenStatesApiClient, @@ -143,9 +142,7 @@ export async function handleMessageV2( : {}), ...(purposeEntry && agreementEntry ? { - agreementId: extractAgreementIdFromAgreementPK( - agreementEntry.PK - ), + agreementId: agreementEntry.agreementId, agreementState: agreementEntry.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ diff --git a/packages/authorization-platformstate-writer/src/utils.ts b/packages/authorization-platformstate-writer/src/utils.ts index f2f421cf7c..37ed23c5c2 100644 --- a/packages/authorization-platformstate-writer/src/utils.ts +++ b/packages/authorization-platformstate-writer/src/utils.ts @@ -8,9 +8,6 @@ import { GetItemInput, PutItemCommand, PutItemInput, - QueryCommand, - QueryCommandOutput, - QueryInput, ScanCommand, ScanCommandOutput, ScanInput, @@ -19,7 +16,6 @@ import { } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { - AgreementId, ClientId, clientKind, ClientKind, @@ -27,7 +23,6 @@ import { ClientKindTokenGenStates, genericInternalError, GSIPKClientIdPurposeId, - GSIPKConsumerIdEServiceId, GSIPKClientIdKid, makeGSIPKClientIdPurposeId, makeGSIPKConsumerIdEServiceId, @@ -37,7 +32,6 @@ import { makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, PlatformStatesAgreementPK, - PlatformStatesAgreementGSIAgreement, PlatformStatesCatalogEntry, PlatformStatesClientEntry, PlatformStatesClientPK, @@ -56,6 +50,8 @@ import { makeGSIPKClientIdKid, clientKidPrefix, clientKidPurposePrefix, + PlatformStatesAgreementEntry, + makePlatformStatesAgreementPK, } from "pagopa-interop-models"; import { match } from "ts-pattern"; import { Logger } from "pagopa-interop-commons"; @@ -551,39 +547,34 @@ export const readPlatformCatalogEntry = async ( } }; -export const readPlatformAgreementEntryByGSIPKConsumerIdEServiceId = async ( - gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId, +export const readAgreementEntry = async ( + primaryKey: PlatformStatesAgreementPK, dynamoDBClient: DynamoDBClient -): Promise => { - const input: QueryInput = { - TableName: config.tokenGenerationReadModelTableNamePlatform, - IndexName: "Agreement", - KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, - ExpressionAttributeValues: { - ":gsiValue": { S: gsiPKConsumerIdEServiceId }, +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, }, - ScanIndexForward: false, + TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); - if (!data.Items) { + if (!data.Item) { return undefined; } else { - const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const platformAgreementEntries = z - .array(PlatformStatesAgreementGSIAgreement) - .safeParse(unmarshalledItems); + const unmarshalled = unmarshall(data.Item); + const agreementEntry = PlatformStatesAgreementEntry.safeParse(unmarshalled); - if (platformAgreementEntries.success) { - return platformAgreementEntries.data[0]; - } else { + if (!agreementEntry.success) { throw genericInternalError( - `Unable to parse platform-states agreement entries: result ${JSON.stringify( - platformAgreementEntries - )} ` + `Unable to parse platform-states agreement entry: result ${JSON.stringify( + agreementEntry + )} - data ${JSON.stringify(data)} ` ); } + return agreementEntry.data; } }; @@ -896,23 +887,6 @@ export const extractKidFromTokenGenStatesEntryPK = ( pk: TokenGenerationStatesClientKidPK | TokenGenerationStatesClientKidPurposePK ): string => pk.split("#")[2]; -export const extractAgreementIdFromAgreementPK = ( - pk: PlatformStatesAgreementPK -): AgreementId => { - const substrings = pk.split("#"); - const agreementId = substrings[1]; - const result = AgreementId.safeParse(agreementId); - - if (!result.success) { - throw genericInternalError( - `Unable to parse agreement PK: result ${JSON.stringify( - result - )} - data ${JSON.stringify(agreementId)} ` - ); - } - return result.data; -}; - export const extractKidFromGSIClientKid = ( GSIPK_clientId_kid: GSIPKClientIdKid ): string => GSIPK_clientId_kid.split("#")[1]; @@ -923,7 +897,7 @@ export const retrievePlatformStatesByPurpose = async ( logger: Logger ): Promise<{ purposeEntry?: PlatformStatesPurposeEntry; - agreementEntry?: PlatformStatesAgreementGSIAgreement; + agreementEntry?: PlatformStatesAgreementEntry; catalogEntry?: PlatformStatesCatalogEntry; }> => { const purposePK = makePlatformStatesPurposePK(purposeId); @@ -938,16 +912,12 @@ export const retrievePlatformStatesByPurpose = async ( }; } - const agreementGSI = makeGSIPKConsumerIdEServiceId({ - eserviceId: purposeEntry.purposeEserviceId, + const agreementPK = makePlatformStatesAgreementPK({ consumerId: purposeEntry.purposeConsumerId, + eserviceId: purposeEntry.purposeEserviceId, }); - const agreementEntry = - await readPlatformAgreementEntryByGSIPKConsumerIdEServiceId( - agreementGSI, - dynamoDBClient - ); + const agreementEntry = await readAgreementEntry(agreementPK, dynamoDBClient); if (!agreementEntry) { return { @@ -1069,7 +1039,7 @@ export const updateTokenGenStatesDataForSecondRetrieval = async ({ entry: TokenGenerationStatesConsumerClient; logger: Logger; purposeEntry?: PlatformStatesPurposeEntry; - agreementEntry?: PlatformStatesAgreementGSIAgreement; + agreementEntry?: PlatformStatesAgreementEntry; catalogEntry?: PlatformStatesCatalogEntry; }): Promise => { const setIfChanged = ( @@ -1228,7 +1198,7 @@ export const createTokenGenStatesConsumerClient = ({ clientId: ClientId; purposeId: PurposeId; purposeEntry?: PlatformStatesPurposeEntry; - agreementEntry?: PlatformStatesAgreementGSIAgreement; + agreementEntry?: PlatformStatesAgreementEntry; catalogEntry?: PlatformStatesCatalogEntry; }): TokenGenerationStatesConsumerClient => { const pk = makeTokenGenerationStatesClientKidPurposePK({ @@ -1260,7 +1230,7 @@ export const createTokenGenStatesConsumerClient = ({ }), ...(purposeEntry && agreementEntry && { - agreementId: extractAgreementIdFromAgreementPK(agreementEntry.PK), + agreementId: agreementEntry.agreementId, agreementState: agreementEntry.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ eserviceId: purposeEntry.purposeEserviceId, diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts index 50ca61fe6b..89621939b4 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { afterAll, afterEach, @@ -395,6 +396,7 @@ describe("integration tests V1 events", async () => { const consumerId = generateId(); const purpose1: Purpose = { ...getMockPurpose(), + consumerId, versions: [getMockPurposeVersion(purposeVersionState.active)], }; const purpose2: Purpose = { @@ -453,17 +455,27 @@ describe("integration tests V1 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -471,17 +483,27 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -615,12 +637,14 @@ describe("integration tests V1 events", async () => { kid: addedKey.kid, purposeId: purpose1.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry1.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement1.eserviceId, + }), agreementId: agreement1.id, agreementState: platformAgreementEntry1.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose1.eserviceId, + eserviceId: agreement1.eserviceId, descriptorId: descriptor1.id, }), descriptorState: previousDescriptorEntry1.state, @@ -649,12 +673,14 @@ describe("integration tests V1 events", async () => { kid: addedKey.kid, purposeId: purpose2.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, @@ -753,17 +779,27 @@ describe("integration tests V1 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -771,17 +807,27 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -969,12 +1015,14 @@ describe("integration tests V1 events", async () => { kid: addedKey.kid, purposeId: purpose1.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry1.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement1.eserviceId, + }), agreementId: agreement1.id, agreementState: platformAgreementEntry1.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose1.eserviceId, + eserviceId: agreement1.eserviceId, descriptorId: descriptor1.id, }), descriptorState: previousDescriptorEntry1.state, @@ -1003,12 +1051,14 @@ describe("integration tests V1 events", async () => { kid: addedKey.kid, purposeId: purpose2.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, @@ -1834,15 +1884,15 @@ describe("integration tests V1 events", async () => { eserviceId: purpose.eserviceId, }; const platformAgreementEntry: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId, - eserviceId: purpose.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; await writePlatformAgreementEntry(platformAgreementEntry, dynamoDBClient); @@ -1954,12 +2004,14 @@ describe("integration tests V1 events", async () => { GSIPK_purposeId: purpose.id, purposeState: platformPurposeEntry.state, purposeVersionId: platformPurposeEntry.purposeVersionId, - GSIPK_consumerId_eserviceId: - platformAgreementEntry.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement.eserviceId, + }), agreementId: agreement.id, agreementState: platformAgreementEntry.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose.eserviceId, + eserviceId: agreement.eserviceId, descriptorId: descriptor.id, }), descriptorState: previousDescriptorEntry.state, @@ -2067,17 +2119,27 @@ describe("integration tests V1 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -2085,17 +2147,27 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -2238,8 +2310,10 @@ describe("integration tests V1 events", async () => { GSIPK_purposeId: purpose2.id, purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ @@ -2353,17 +2427,27 @@ describe("integration tests V1 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -2371,17 +2455,27 @@ describe("integration tests V1 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -2566,12 +2660,14 @@ describe("integration tests V1 events", async () => { GSIPK_purposeId: purpose2.id, purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, diff --git a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts index 22257b7c93..e505fa3f11 100644 --- a/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/authorization-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { afterAll, afterEach, @@ -227,17 +228,27 @@ describe("integration tests V2 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -245,17 +256,27 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -269,7 +290,7 @@ describe("integration tests V2 events", async () => { }; const previousDescriptorEntry1: PlatformStatesCatalogEntry = { PK: makePlatformStatesEServiceDescriptorPK({ - eserviceId: purpose1.eserviceId, + eserviceId: agreement1.eserviceId, descriptorId: descriptor1.id, }), state: itemState.active, @@ -286,7 +307,7 @@ describe("integration tests V2 events", async () => { }; const previousDescriptorEntry2: PlatformStatesCatalogEntry = { PK: makePlatformStatesEServiceDescriptorPK({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), state: itemState.active, @@ -384,12 +405,14 @@ describe("integration tests V2 events", async () => { kid: addedKey.kid, purposeId: purpose1.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry1.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement1.eserviceId, + }), agreementId: agreement1.id, agreementState: platformAgreementEntry1.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose1.eserviceId, + eserviceId: agreement1.eserviceId, descriptorId: descriptor1.id, }), descriptorState: previousDescriptorEntry1.state, @@ -418,12 +441,14 @@ describe("integration tests V2 events", async () => { kid: addedKey.kid, purposeId: purpose2.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, @@ -517,17 +542,27 @@ describe("integration tests V2 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -535,17 +570,27 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -682,12 +727,14 @@ describe("integration tests V2 events", async () => { kid: addedKey.kid, purposeId: purpose1.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry1.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement1.eserviceId, + }), agreementId: agreement1.id, agreementState: platformAgreementEntry1.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose1.eserviceId, + eserviceId: agreement1.eserviceId, descriptorId: descriptor1.id, }), descriptorState: previousDescriptorEntry1.state, @@ -716,12 +763,14 @@ describe("integration tests V2 events", async () => { kid: addedKey.kid, purposeId: purpose2.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, @@ -815,17 +864,27 @@ describe("integration tests V2 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -833,17 +892,27 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -1032,12 +1101,14 @@ describe("integration tests V2 events", async () => { kid: addedKey.kid, purposeId: purpose1.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry1.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement1.eserviceId, + }), agreementId: agreement1.id, agreementState: platformAgreementEntry1.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose1.eserviceId, + eserviceId: agreement1.eserviceId, descriptorId: descriptor1.id, }), descriptorState: previousDescriptorEntry1.state, @@ -1066,12 +1137,14 @@ describe("integration tests V2 events", async () => { kid: addedKey.kid, purposeId: purpose2.id, }), - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, @@ -1860,17 +1933,23 @@ describe("integration tests V2 events", async () => { ...getMockAgreement(), consumerId, eserviceId: purpose.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, }; const platformAgreementEntry: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose.consumerId, - eserviceId: purpose.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; await writePlatformAgreementEntry(platformAgreementEntry, dynamoDBClient); @@ -1982,8 +2061,10 @@ describe("integration tests V2 events", async () => { GSIPK_purposeId: purpose.id, purposeState: platformPurposeEntry.state, purposeVersionId: platformPurposeEntry.purposeVersionId, - GSIPK_consumerId_eserviceId: - platformAgreementEntry.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement.eserviceId, + }), agreementId: agreement.id, agreementState: platformAgreementEntry.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ @@ -2090,17 +2171,27 @@ describe("integration tests V2 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -2108,17 +2199,27 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -2261,12 +2362,14 @@ describe("integration tests V2 events", async () => { GSIPK_purposeId: purpose2.id, purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, @@ -2372,17 +2475,27 @@ describe("integration tests V2 events", async () => { }; await writePlatformPurposeEntry(platformPurposeEntry2, dynamoDBClient); - const agreement1 = getMockAgreement(); + const agreement1: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose1.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement1.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement1.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose1.consumerId, - eserviceId: purpose1.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement1.id, + agreementTimestamp: agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, }; await writePlatformAgreementEntry( @@ -2390,17 +2503,27 @@ describe("integration tests V2 events", async () => { dynamoDBClient ); - const agreement2 = getMockAgreement(); + const agreement2: Agreement = { + ...getMockAgreement(), + consumerId, + eserviceId: purpose2.eserviceId, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; const platformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement2.id), + PK: makePlatformStatesAgreementPK({ + consumerId, + eserviceId: agreement2.eserviceId, + }), version: 1, state: itemState.active, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: purpose2.consumerId, - eserviceId: purpose2.eserviceId, - }), - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: agreement2.id, + agreementTimestamp: agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, }; await writePlatformAgreementEntry( @@ -2585,12 +2708,14 @@ describe("integration tests V2 events", async () => { GSIPK_purposeId: purpose2.id, purposeState: platformPurposeEntry2.state, purposeVersionId: platformPurposeEntry2.purposeVersionId, - GSIPK_consumerId_eserviceId: - platformAgreementEntry2.GSIPK_consumerId_eserviceId, + GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + consumerId, + eserviceId: agreement2.eserviceId, + }), agreementId: agreement2.id, agreementState: platformAgreementEntry2.state, GSIPK_eserviceId_descriptorId: makeGSIPKEServiceIdDescriptorId({ - eserviceId: purpose2.eserviceId, + eserviceId: agreement2.eserviceId, descriptorId: descriptor2.id, }), descriptorState: previousDescriptorEntry2.state, diff --git a/packages/authorization-platformstate-writer/test/utils.test.ts b/packages/authorization-platformstate-writer/test/utils.test.ts index 6c03fb3777..530f197849 100644 --- a/packages/authorization-platformstate-writer/test/utils.test.ts +++ b/packages/authorization-platformstate-writer/test/utils.test.ts @@ -42,7 +42,6 @@ import { makePlatformStatesPurposePK, makeTokenGenerationStatesClientKidPK, makeTokenGenerationStatesClientKidPurposePK, - PlatformStatesAgreementGSIAgreement, PlatformStatesAgreementEntry, PlatformStatesCatalogEntry, PlatformStatesClientEntry, @@ -72,7 +71,6 @@ import { convertEntriesToClientKidInTokenGenerationStates, deleteClientEntryFromPlatformStates, readConsumerClientsInTokenGenStatesV1, - readPlatformAgreementEntryByGSIPKConsumerIdEServiceId, retrievePlatformStatesByPurpose, updateTokenGenStatesDataForSecondRetrieval, upsertPlatformClientEntry, @@ -826,45 +824,6 @@ describe("utils", () => { }); }); - it("readPlatformAgreementEntryByGSIPKConsumerIdEServiceId", async () => { - const pk1 = makePlatformStatesAgreementPK(generateId()); - const pk2 = makePlatformStatesAgreementPK(generateId()); - const pk3 = makePlatformStatesAgreementPK(generateId()); - - const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), - }); - - const threeHoursAgo = new Date(); - threeHoursAgo.setHours(threeHoursAgo.getHours() - 3); - - const agreementEntry1: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(pk1, GSIPK_consumerId_eserviceId), - GSISK_agreementTimestamp: threeHoursAgo.toISOString(), - }; - - const agreementEntry2: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(pk2, GSIPK_consumerId_eserviceId), - GSISK_agreementTimestamp: new Date().toISOString(), - }; - - const agreementEntry3 = getMockPlatformStatesAgreementEntry(pk3); - - await writePlatformAgreementEntry(agreementEntry1, dynamoDBClient); - await writePlatformAgreementEntry(agreementEntry2, dynamoDBClient); - await writePlatformAgreementEntry(agreementEntry3, dynamoDBClient); - - const res = await readPlatformAgreementEntryByGSIPKConsumerIdEServiceId( - GSIPK_consumerId_eserviceId, - dynamoDBClient - ); - - expect(res).toEqual( - PlatformStatesAgreementGSIAgreement.parse(agreementEntry2) - ); - }); - describe("upsertTokenGenStatesConsumerClient", () => { it("write", async () => { const tokenGenStatesConsumerClient = @@ -1009,14 +968,13 @@ describe("utils", () => { await writePlatformPurposeEntry(purposeEntry, dynamoDBClient); - const agreementPK = makePlatformStatesAgreementPK(agreementId); + const agreementPK = makePlatformStatesAgreementPK({ + consumerId, + eserviceId, + }); const agreementEntry: PlatformStatesAgreementEntry = { - ...getMockPlatformStatesAgreementEntry(agreementPK), + ...getMockPlatformStatesAgreementEntry(agreementPK, agreementId), agreementDescriptorId: descriptorId, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId, - eserviceId, - }), }; await writePlatformAgreementEntry(agreementEntry, dynamoDBClient); @@ -1044,7 +1002,7 @@ describe("utils", () => { expect(res).toEqual({ purposeEntry, - agreementEntry: PlatformStatesAgreementGSIAgreement.parse(agreementEntry), + agreementEntry, catalogEntry, }); }); @@ -1183,7 +1141,7 @@ describe("utils", () => { ); }); - it("updateTokenDataForSecondRetrieval", async () => { + it("updateTokenGenStatesDataForSecondRetrieval", async () => { const consumerId = generateId(); const descriptor = getMockDescriptor(); const eservice: EService = { @@ -1256,15 +1214,15 @@ describe("utils", () => { }; const platformAgreementEntry: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement.id), - version: 1, - state: itemState.inactive, - updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + PK: makePlatformStatesAgreementPK({ consumerId, eserviceId: eservice.id, }), - GSISK_agreementTimestamp: new Date().toISOString(), + version: 1, + state: itemState.inactive, + updatedAt: new Date().toISOString(), + agreementId: agreement.id, + agreementTimestamp: agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, }; diff --git a/packages/commons-test/src/setupDynamoDBtables.ts b/packages/commons-test/src/setupDynamoDBtables.ts index 5058539c5b..a20a294c20 100644 --- a/packages/commons-test/src/setupDynamoDBtables.ts +++ b/packages/commons-test/src/setupDynamoDBtables.ts @@ -11,32 +11,9 @@ export const buildDynamoDBTables = async ( ): Promise => { const platformTableDefinition: CreateTableInput = { TableName: "platform-states", - AttributeDefinitions: [ - { AttributeName: "PK", AttributeType: "S" }, - { AttributeName: "GSIPK_consumerId_eserviceId", AttributeType: "S" }, - { AttributeName: "GSISK_agreementTimestamp", AttributeType: "S" }, - ], + AttributeDefinitions: [{ AttributeName: "PK", AttributeType: "S" }], KeySchema: [{ AttributeName: "PK", KeyType: "HASH" }], BillingMode: "PAY_PER_REQUEST", - GlobalSecondaryIndexes: [ - { - IndexName: "Agreement", - KeySchema: [ - { - AttributeName: "GSIPK_consumerId_eserviceId", - KeyType: "HASH", - }, - { - AttributeName: "GSISK_agreementTimestamp", - KeyType: "RANGE", - }, - ], - Projection: { - ProjectionType: "INCLUDE", - NonKeyAttributes: ["state", "agreementDescriptorId"], - }, - }, - ], }; const command1 = new CreateTableCommand(platformTableDefinition); await dynamoDBClient.send(command1); diff --git a/packages/commons-test/src/testUtils.ts b/packages/commons-test/src/testUtils.ts index da8f794901..174ee9c41f 100644 --- a/packages/commons-test/src/testUtils.ts +++ b/packages/commons-test/src/testUtils.ts @@ -58,7 +58,6 @@ import { TenantFeatureCertifier, TenantFeature, DescriptorState, - GSIPKConsumerIdEServiceId, PlatformStatesAgreementEntry, PlatformStatesAgreementPK, makeGSIPKClientIdKid, @@ -512,19 +511,14 @@ export const getMockTokenGenStatesConsumerClient = ( export const getMockPlatformStatesAgreementEntry = ( primaryKey: PlatformStatesAgreementPK, - GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId( - { - consumerId: generateId(), - eserviceId: generateId(), - } - ) + agreementId: AgreementId ): PlatformStatesAgreementEntry => ({ PK: primaryKey, state: itemState.inactive, version: 1, updatedAt: new Date().toISOString(), - GSIPK_consumerId_eserviceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId, + agreementTimestamp: new Date().toISOString(), agreementDescriptorId: generateId(), }); diff --git a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts index 3ba49a0502..5b9b6e1325 100644 --- a/packages/commons-test/src/tokenGenerationReadmodelUtils.ts +++ b/packages/commons-test/src/tokenGenerationReadmodelUtils.ts @@ -450,11 +450,11 @@ export const writePlatformAgreementEntry = async ( updatedAt: { S: agreementEntry.updatedAt, }, - GSIPK_consumerId_eserviceId: { - S: agreementEntry.GSIPK_consumerId_eserviceId, + agreementId: { + S: agreementEntry.agreementId, }, - GSISK_agreementTimestamp: { - S: agreementEntry.GSISK_agreementTimestamp, + agreementTimestamp: { + S: agreementEntry.agreementTimestamp, }, agreementDescriptorId: { S: agreementEntry.agreementDescriptorId, diff --git a/packages/models/src/brandedIds.ts b/packages/models/src/brandedIds.ts index 29204275fb..1b47d9bbfe 100644 --- a/packages/models/src/brandedIds.ts +++ b/packages/models/src/brandedIds.ts @@ -96,7 +96,7 @@ const agreementPrefix = "AGREEMENT#"; export const PlatformStatesAgreementPK = z .string() .refine((pk) => pk.startsWith(agreementPrefix)) - .brand(`${agreementPrefix}agreementId`); + .brand(`${agreementPrefix}consumerId#eserviceId`); export type PlatformStatesAgreementPK = z.infer< typeof PlatformStatesAgreementPK >; @@ -150,7 +150,7 @@ export type GSIPKEServiceIdDescriptorId = z.infer< export const GSIPKClientIdPurposeId = z.string().brand(`clientId#purposeId`); export type GSIPKClientIdPurposeId = z.infer; -export const GSIPKClientIdKid = z.string().brand("kid"); +export const GSIPKClientIdKid = z.string().brand("clientId#kid"); export type GSIPKClientIdKid = z.infer; type IDS = diff --git a/packages/models/src/token-generation-readmodel/commons.ts b/packages/models/src/token-generation-readmodel/commons.ts index 67e42f85b0..524e39f454 100644 --- a/packages/models/src/token-generation-readmodel/commons.ts +++ b/packages/models/src/token-generation-readmodel/commons.ts @@ -2,7 +2,6 @@ import { z } from "zod"; import { EServiceId, DescriptorId, - AgreementId, PurposeId, ClientId, TenantId, @@ -10,13 +9,13 @@ import { PlatformStatesAgreementPK, PlatformStatesPurposePK, PlatformStatesClientPK, - GSIPKConsumerIdEServiceId, TokenGenerationStatesClientKidPurposePK, TokenGenerationStatesClientKidPK, GSIPKEServiceIdDescriptorId, GSIPKClientIdPurposeId, unsafeBrandId, GSIPKClientIdKid, + GSIPKConsumerIdEServiceId, } from "../brandedIds.js"; export const makePlatformStatesEServiceDescriptorPK = ({ @@ -30,10 +29,16 @@ export const makePlatformStatesEServiceDescriptorPK = ({ `ESERVICEDESCRIPTOR#${eserviceId}#${descriptorId}` ); -export const makePlatformStatesAgreementPK = ( - agreementId: AgreementId -): PlatformStatesAgreementPK => - unsafeBrandId(`AGREEMENT#${agreementId}`); +export const makePlatformStatesAgreementPK = ({ + consumerId, + eserviceId, +}: { + consumerId: TenantId; + eserviceId: EServiceId; +}): PlatformStatesAgreementPK => + unsafeBrandId( + `AGREEMENT#${consumerId}#${eserviceId}` + ); export const makePlatformStatesPurposePK = ( purposeId: PurposeId diff --git a/packages/models/src/token-generation-readmodel/platform-states-entry.ts b/packages/models/src/token-generation-readmodel/platform-states-entry.ts index a222482ac0..4d66423a7f 100644 --- a/packages/models/src/token-generation-readmodel/platform-states-entry.ts +++ b/packages/models/src/token-generation-readmodel/platform-states-entry.ts @@ -1,8 +1,8 @@ import { z } from "zod"; import { + AgreementId, DescriptorId, EServiceId, - GSIPKConsumerIdEServiceId, PlatformStatesAgreementPK, PlatformStatesClientPK, PlatformStatesEServiceDescriptorPK, @@ -51,8 +51,8 @@ export type PlatformStatesPurposeEntry = z.infer< export const PlatformStatesAgreementEntry = PlatformStatesBaseEntry.extend({ PK: PlatformStatesAgreementPK, - GSIPK_consumerId_eserviceId: GSIPKConsumerIdEServiceId, - GSISK_agreementTimestamp: z.string().datetime(), + agreementId: AgreementId, + agreementTimestamp: z.string().datetime(), agreementDescriptorId: DescriptorId, }); export type PlatformStatesAgreementEntry = z.infer< @@ -77,16 +77,3 @@ export const PlatformStatesGenericEntry = PlatformStatesCatalogEntry.or( export type PlatformStatesGenericEntry = z.infer< typeof PlatformStatesGenericEntry >; - -// GSI projection types -export const PlatformStatesAgreementGSIAgreement = - PlatformStatesAgreementEntry.pick({ - PK: true, - GSIPK_consumerId_eserviceId: true, - GSISK_agreementTimestamp: true, - agreementDescriptorId: true, - state: true, - }); -export type PlatformStatesAgreementGSIAgreement = z.infer< - typeof PlatformStatesAgreementGSIAgreement ->; diff --git a/packages/purpose-platformstate-writer/src/utils.ts b/packages/purpose-platformstate-writer/src/utils.ts index bd8cc7c04b..fd00e56844 100644 --- a/packages/purpose-platformstate-writer/src/utils.ts +++ b/packages/purpose-platformstate-writer/src/utils.ts @@ -16,15 +16,14 @@ import { } from "@aws-sdk/client-dynamodb"; import { unmarshall } from "@aws-sdk/util-dynamodb"; import { - AgreementId, genericInternalError, - GSIPKConsumerIdEServiceId, itemState, ItemState, makeGSIPKConsumerIdEServiceId, makeGSIPKEServiceIdDescriptorId, + makePlatformStatesAgreementPK, makePlatformStatesEServiceDescriptorPK, - PlatformStatesAgreementGSIAgreement, + PlatformStatesAgreementEntry, PlatformStatesAgreementPK, PlatformStatesCatalogEntry, PlatformStatesEServiceDescriptorPK, @@ -253,14 +252,19 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = purpose.id, exclusiveStartKey ); - const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ + const GSIPK_consumerId_eserviceId = makeGSIPKConsumerIdEServiceId({ consumerId: purpose.consumerId, eserviceId: purpose.eserviceId, }); - const platformAgreementEntry = await readPlatformAgreementEntry( - dynamoDBClient, - gsiPKConsumerIdEServiceId + const agreementPlatformStatesPK = makePlatformStatesAgreementPK({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); + + const platformAgreementEntry = await readAgreementEntry( + agreementPlatformStatesPK, + dynamoDBClient ); const { catalogEntryPK, gsiPKEServiceIdDescriptorId } = @@ -301,7 +305,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = if (isAgreementMissingInTokenGenStates) { logger.info( - `Adding agreement info to token-generation-states entry with PK ${tokenEntryPK} and GSIPK_consumerId_eserviceId ${gsiPKConsumerIdEServiceId}` + `Adding agreement info to token-generation-states entry with PK ${tokenEntryPK} and GSIPK_consumerId_eserviceId ${GSIPK_consumerId_eserviceId}` ); } // Agreement data from platform-states @@ -311,7 +315,7 @@ export const updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData = > = isAgreementMissingInTokenGenStates ? { ":agreementId": { - S: extractAgreementIdFromAgreementPK(platformAgreementEntry.PK), + S: platformAgreementEntry.agreementId, }, ":agreementState": { S: platformAgreementEntry.state, @@ -499,39 +503,34 @@ export const updatePurposeDataInTokenGenStatesEntries = async ({ ); }; -export const readPlatformAgreementEntry = async ( - dynamoDBClient: DynamoDBClient, - gsiPKConsumerIdEServiceId: GSIPKConsumerIdEServiceId -): Promise => { - const input: QueryInput = { - TableName: config.tokenGenerationReadModelTableNamePlatform, - IndexName: "Agreement", - KeyConditionExpression: `GSIPK_consumerId_eserviceId = :gsiValue`, - ExpressionAttributeValues: { - ":gsiValue": { S: gsiPKConsumerIdEServiceId }, +export const readAgreementEntry = async ( + primaryKey: PlatformStatesAgreementPK, + dynamoDBClient: DynamoDBClient +): Promise => { + const input: GetItemInput = { + Key: { + PK: { S: primaryKey }, }, - ScanIndexForward: false, + TableName: config.tokenGenerationReadModelTableNamePlatform, + ConsistentRead: true, }; - const command = new QueryCommand(input); - const data: QueryCommandOutput = await dynamoDBClient.send(command); + const command = new GetItemCommand(input); + const data: GetItemCommandOutput = await dynamoDBClient.send(command); - if (!data.Items) { + if (!data.Item) { return undefined; } else { - const unmarshalledItems = data.Items.map((item) => unmarshall(item)); - const platformAgreementEntries = z - .array(PlatformStatesAgreementGSIAgreement) - .safeParse(unmarshalledItems); + const unmarshalled = unmarshall(data.Item); + const agreementEntry = PlatformStatesAgreementEntry.safeParse(unmarshalled); - if (platformAgreementEntries.success) { - return platformAgreementEntries.data[0]; - } else { + if (!agreementEntry.success) { throw genericInternalError( - `Unable to parse platform-states agreement entries: result ${JSON.stringify( - platformAgreementEntries - )} ` + `Unable to parse platform-states agreement entry: result ${JSON.stringify( + agreementEntry + )} - data ${JSON.stringify(data)} ` ); } + return agreementEntry.data; } }; @@ -590,20 +589,3 @@ export const getLastArchivedPurposeVersion = ( purposeVersions .filter((v) => v.state === purposeVersionState.archived) .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0]; - -const extractAgreementIdFromAgreementPK = ( - pk: PlatformStatesAgreementPK -): AgreementId => { - const substrings = pk.split("#"); - const agreementId = substrings[1]; - const result = AgreementId.safeParse(agreementId); - - if (!result.success) { - throw genericInternalError( - `Unable to parse agreement PK: result ${JSON.stringify( - result - )} - data ${JSON.stringify(agreementId)} ` - ); - } - return result.data; -}; diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts index 68d52b7a64..1f1498d7a9 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV1.integration.test.ts @@ -14,9 +14,12 @@ import { getMockPurpose, getMockPurposeVersion, readAllTokenGenStatesItems, + writePlatformAgreementEntry, + writePlatformCatalogEntry, writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test"; import { + Agreement, generateId, itemState, makeGSIPKConsumerIdEServiceId, @@ -50,11 +53,7 @@ import { readPlatformPurposeEntry, writePlatformPurposeEntry, } from "../src/utils.js"; -import { - dynamoDBClient, - writeAgreementEntry, - writeCatalogEntry, -} from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests for events V1", () => { beforeEach(async () => { @@ -625,13 +624,20 @@ describe("integration tests for events V1", () => { // platform-states const mockDescriptor = getMockDescriptor(); - const mockAgreement = { + const mockAgreement: Agreement = { ...getMockAgreement(purpose.eserviceId, purpose.consumerId), descriptorId: mockDescriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, }; - const catalogAgreementEntryPK = makePlatformStatesAgreementPK( - mockAgreement.id - ); + const catalogAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: mockAgreement.consumerId, eserviceId: mockAgreement.eserviceId, @@ -639,13 +645,14 @@ describe("integration tests for events V1", () => { const previousAgreementEntry: PlatformStatesAgreementEntry = { PK: catalogAgreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: mockAgreement.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreementTimestamp: mockAgreement.stamps.activation!.when.toISOString(), agreementDescriptorId: mockAgreement.descriptorId, version: 2, updatedAt: new Date().toISOString(), }; - await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousAgreementEntry, dynamoDBClient); const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ eserviceId: purpose.eserviceId, @@ -659,7 +666,7 @@ describe("integration tests for events V1", () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(dynamoDBClient, previousDescriptorEntry); + await writePlatformCatalogEntry(previousDescriptorEntry, dynamoDBClient); // token-generation-states const purposeId = purpose.id; diff --git a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts index 77990c7122..e07b0f8036 100644 --- a/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts +++ b/packages/purpose-platformstate-writer/test/consumerServiceV2.integration.test.ts @@ -16,9 +16,12 @@ import { getMockPurpose, getMockPurposeVersion, readAllTokenGenStatesItems, + writePlatformAgreementEntry, + writePlatformCatalogEntry, writeTokenGenStatesConsumerClient, } from "pagopa-interop-commons-test/index.js"; import { + Agreement, generateId, itemState, makeGSIPKConsumerIdEServiceId, @@ -53,11 +56,7 @@ import { readPlatformPurposeEntry, writePlatformPurposeEntry, } from "../src/utils.js"; -import { - writeAgreementEntry, - writeCatalogEntry, - dynamoDBClient, -} from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("integration tests for events V2", () => { beforeEach(async () => { @@ -477,13 +476,20 @@ describe("integration tests for events V2", () => { // platform-states const mockDescriptor = getMockDescriptor(); - const mockAgreement = { + const mockAgreement: Agreement = { ...getMockAgreement(purpose.eserviceId, purpose.consumerId), descriptorId: mockDescriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, }; - const catalogAgreementEntryPK = makePlatformStatesAgreementPK( - mockAgreement.id - ); + const catalogAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: mockAgreement.consumerId, eserviceId: mockAgreement.eserviceId, @@ -491,13 +497,14 @@ describe("integration tests for events V2", () => { const previousAgreementEntry: PlatformStatesAgreementEntry = { PK: catalogAgreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: mockAgreement.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreementTimestamp: mockAgreement.stamps.activation!.when.toISOString(), agreementDescriptorId: mockAgreement.descriptorId, version: 2, updatedAt: new Date().toISOString(), }; - await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousAgreementEntry, dynamoDBClient); const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ eserviceId: purpose.eserviceId, @@ -511,7 +518,7 @@ describe("integration tests for events V2", () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(dynamoDBClient, previousDescriptorEntry); + await writePlatformCatalogEntry(previousDescriptorEntry, dynamoDBClient); // token-generation-states const purposeId = purpose.id; @@ -1265,13 +1272,20 @@ describe("integration tests for events V2", () => { // platform-states const mockDescriptor = getMockDescriptor(); - const mockAgreement = { + const mockAgreement: Agreement = { ...getMockAgreement(purpose.eserviceId, purpose.consumerId), descriptorId: mockDescriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, }; - const catalogAgreementEntryPK = makePlatformStatesAgreementPK( - mockAgreement.id - ); + const catalogAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: mockAgreement.consumerId, eserviceId: mockAgreement.eserviceId, @@ -1279,13 +1293,14 @@ describe("integration tests for events V2", () => { const previousAgreementEntry: PlatformStatesAgreementEntry = { PK: catalogAgreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: mockAgreement.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreementTimestamp: mockAgreement.stamps.activation!.when.toISOString(), agreementDescriptorId: mockAgreement.descriptorId, version: 2, updatedAt: new Date().toISOString(), }; - await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousAgreementEntry, dynamoDBClient); const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ eserviceId: purpose.eserviceId, @@ -1299,7 +1314,7 @@ describe("integration tests for events V2", () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(dynamoDBClient, previousDescriptorEntry); + await writePlatformCatalogEntry(previousDescriptorEntry, dynamoDBClient); // token-generation-states const purposeId = purpose.id; diff --git a/packages/purpose-platformstate-writer/test/utils.test.ts b/packages/purpose-platformstate-writer/test/utils.test.ts index c98c8dc98f..0b8af9ca09 100644 --- a/packages/purpose-platformstate-writer/test/utils.test.ts +++ b/packages/purpose-platformstate-writer/test/utils.test.ts @@ -29,7 +29,7 @@ import { makeTokenGenerationStatesClientKidPurposePK, purposeVersionState, TokenGenStatesConsumerClientGSIPurpose, - PlatformStatesAgreementGSIAgreement, + Agreement, } from "pagopa-interop-models"; import { ConditionalCheckFailedException } from "@aws-sdk/client-dynamodb"; import { @@ -42,12 +42,13 @@ import { getMockPurpose, getMockDescriptor, getMockAgreement, + writePlatformAgreementEntry, + writePlatformCatalogEntry, } from "pagopa-interop-commons-test"; import { genericLogger } from "pagopa-interop-commons"; import { deletePlatformPurposeEntry, getPurposeStateFromPurposeVersions, - readPlatformAgreementEntry, readPlatformPurposeEntry, readTokenGenStatesEntriesByGSIPKPurposeId, updatePurposeDataInPlatformStatesEntry, @@ -55,11 +56,7 @@ import { updateTokenGenStatesEntriesWithPurposeAndPlatformStatesData, writePlatformPurposeEntry, } from "../src/utils.js"; -import { - writeAgreementEntry, - writeCatalogEntry, - dynamoDBClient, -} from "./utils.js"; +import { dynamoDBClient } from "./utils.js"; describe("utils tests", async () => { beforeEach(async () => { @@ -336,67 +333,6 @@ describe("utils tests", async () => { }); }); - describe("readPlatformAgreementEntry", async () => { - it("should return undefined if entry doesn't exist", async () => { - const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), - }); - const platformAgreementEntry = await readPlatformAgreementEntry( - dynamoDBClient, - gsiPKConsumerIdEServiceId - ); - expect(platformAgreementEntry).toBeUndefined(); - }); - - it("should return the most recent platform-states entry if it exists", async () => { - const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), - }); - const previousPlatformAgreementEntry1: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(generateId()), - state: itemState.inactive, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), - agreementDescriptorId: generateId(), - version: 1, - updatedAt: new Date().toISOString(), - }; - await writeAgreementEntry( - previousPlatformAgreementEntry1, - dynamoDBClient - ); - - const newDate = new Date(); - newDate.setDate(newDate.getDate() + 1); - const previousPlatformAgreementEntry2: PlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(generateId()), - state: itemState.inactive, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: newDate.toISOString(), - agreementDescriptorId: generateId(), - version: 1, - updatedAt: newDate.toISOString(), - }; - await writeAgreementEntry( - previousPlatformAgreementEntry2, - dynamoDBClient - ); - - const retrievedPlatformAgreementEntry = await readPlatformAgreementEntry( - dynamoDBClient, - gsiPKConsumerIdEServiceId - ); - - expect(retrievedPlatformAgreementEntry).toEqual( - PlatformStatesAgreementGSIAgreement.parse( - previousPlatformAgreementEntry2 - ) - ); - }); - }); - describe("updatePurposeDataInPlatformStatesEntry", async () => { it("should throw error if previous entry doesn't exist", async () => { const purposeId = generateId(); @@ -748,13 +684,20 @@ describe("utils tests", async () => { // platform-states const mockDescriptor = getMockDescriptor(); - const mockAgreement = { + const mockAgreement: Agreement = { ...getMockAgreement(purpose.eserviceId, purpose.consumerId), descriptorId: mockDescriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, }; - const catalogAgreementEntryPK = makePlatformStatesAgreementPK( - mockAgreement.id - ); + const catalogAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: mockAgreement.consumerId, eserviceId: mockAgreement.eserviceId, @@ -762,13 +705,14 @@ describe("utils tests", async () => { const previousAgreementEntry: PlatformStatesAgreementEntry = { PK: catalogAgreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: mockAgreement.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreementTimestamp: mockAgreement.stamps.activation!.when.toISOString(), agreementDescriptorId: mockAgreement.descriptorId, version: 2, updatedAt: new Date().toISOString(), }; - await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousAgreementEntry, dynamoDBClient); // token-generation-states const purposeId = purpose.id; @@ -865,13 +809,20 @@ describe("utils tests", async () => { // platform-states const mockDescriptor = getMockDescriptor(); - const mockAgreement = { + const mockAgreement: Agreement = { ...getMockAgreement(purpose.eserviceId, purpose.consumerId), descriptorId: mockDescriptor.id, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, }; - const catalogAgreementEntryPK = makePlatformStatesAgreementPK( - mockAgreement.id - ); + const catalogAgreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: purpose.consumerId, + eserviceId: purpose.eserviceId, + }); const gsiPKConsumerIdEServiceId = makeGSIPKConsumerIdEServiceId({ consumerId: mockAgreement.consumerId, eserviceId: mockAgreement.eserviceId, @@ -879,13 +830,14 @@ describe("utils tests", async () => { const previousAgreementEntry: PlatformStatesAgreementEntry = { PK: catalogAgreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: gsiPKConsumerIdEServiceId, - GSISK_agreementTimestamp: new Date().toISOString(), + agreementId: mockAgreement.id, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreementTimestamp: mockAgreement.stamps.activation!.when.toISOString(), agreementDescriptorId: mockAgreement.descriptorId, version: 2, updatedAt: new Date().toISOString(), }; - await writeAgreementEntry(previousAgreementEntry, dynamoDBClient); + await writePlatformAgreementEntry(previousAgreementEntry, dynamoDBClient); const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ eserviceId: purpose.eserviceId, @@ -899,7 +851,7 @@ describe("utils tests", async () => { version: 2, updatedAt: new Date().toISOString(), }; - await writeCatalogEntry(dynamoDBClient, previousDescriptorEntry); + await writePlatformCatalogEntry(previousDescriptorEntry, dynamoDBClient); // token-generation-states const purposeId = purpose.id; diff --git a/packages/purpose-platformstate-writer/test/utils.ts b/packages/purpose-platformstate-writer/test/utils.ts index a1c6c333d2..b445e6374b 100644 --- a/packages/purpose-platformstate-writer/test/utils.ts +++ b/packages/purpose-platformstate-writer/test/utils.ts @@ -1,19 +1,5 @@ -import { fail } from "assert"; -import { - DynamoDBClient, - GetItemCommand, - GetItemCommandOutput, - GetItemInput, - PutItemCommand, - PutItemInput, -} from "@aws-sdk/client-dynamodb"; -import { - genericInternalError, - PlatformStatesAgreementEntry, - PlatformStatesCatalogEntry, -} from "pagopa-interop-models"; +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; import { inject } from "vitest"; -import { unmarshall } from "@aws-sdk/util-dynamodb"; const config = inject("tokenGenerationReadModelConfig"); @@ -23,113 +9,3 @@ if (!config) { export const dynamoDBClient = new DynamoDBClient({ endpoint: `http://localhost:${config.tokenGenerationReadModelDbPort}`, }); - -export const writeAgreementEntry = async ( - agreementEntry: PlatformStatesAgreementEntry, - dynamoDBClient: DynamoDBClient -): Promise => { - if (!config) { - fail(); - } - - const input: PutItemInput = { - Item: { - PK: { - S: agreementEntry.PK, - }, - state: { - S: agreementEntry.state, - }, - version: { - N: agreementEntry.version.toString(), - }, - updatedAt: { - S: agreementEntry.updatedAt, - }, - GSIPK_consumerId_eserviceId: { - S: agreementEntry.GSIPK_consumerId_eserviceId, - }, - GSISK_agreementTimestamp: { - S: agreementEntry.GSISK_agreementTimestamp, - }, - agreementDescriptorId: { - S: agreementEntry.agreementDescriptorId, - }, - }, - TableName: config.tokenGenerationReadModelTableNamePlatform, - }; - const command = new PutItemCommand(input); - await dynamoDBClient.send(command); -}; - -export const readAgreementEntry = async ( - primaryKey: string, - dynamoDBClient: DynamoDBClient -): Promise => { - if (!config) { - fail(); - } - - const input: GetItemInput = { - Key: { - PK: { S: primaryKey }, - }, - TableName: config.tokenGenerationReadModelTableNamePlatform, - }; - const command = new GetItemCommand(input); - const data: GetItemCommandOutput = await dynamoDBClient.send(command); - - if (!data.Item) { - return undefined; - } else { - const unmarshalled = unmarshall(data.Item); - const agreementEntry = PlatformStatesAgreementEntry.safeParse(unmarshalled); - - if (!agreementEntry.success) { - throw genericInternalError( - `Unable to parse platform-states agreement entry: result ${JSON.stringify( - agreementEntry - )} - data ${JSON.stringify(data)} ` - ); - } - return agreementEntry.data; - } -}; - -export const writeCatalogEntry = async ( - dynamoDBClient: DynamoDBClient, - catalogEntry: PlatformStatesCatalogEntry -): Promise => { - if (!config) { - fail(); - } - - const input: PutItemInput = { - ConditionExpression: "attribute_not_exists(PK)", - Item: { - PK: { - S: catalogEntry.PK, - }, - state: { - S: catalogEntry.state, - }, - descriptorAudience: { - L: catalogEntry.descriptorAudience.map((item) => ({ - S: item, - })), - }, - descriptorVoucherLifespan: { - N: catalogEntry.descriptorVoucherLifespan.toString(), - }, - version: { - N: catalogEntry.version.toString(), - }, - updatedAt: { - S: catalogEntry.updatedAt, - }, - }, - TableName: config.tokenGenerationReadModelTableNamePlatform, - }; - const command = new PutItemCommand(input); - await dynamoDBClient.send(command); -}; diff --git a/packages/token-generation-readmodel-checker/src/models/types.ts b/packages/token-generation-readmodel-checker/src/models/types.ts index 31f956f39b..e31efc6427 100644 --- a/packages/token-generation-readmodel-checker/src/models/types.ts +++ b/packages/token-generation-readmodel-checker/src/models/types.ts @@ -48,9 +48,9 @@ export const ComparisonPlatformStatesAgreementEntry = PlatformStatesAgreementEntry.pick({ PK: true, state: true, - GSIPK_consumerId_eserviceId: true, + agreementId: true, agreementDescriptorId: true, - GSISK_agreementTimestamp: true, + agreementTimestamp: true, }); export type ComparisonPlatformStatesAgreementEntry = z.infer< typeof ComparisonPlatformStatesAgreementEntry diff --git a/packages/token-generation-readmodel-checker/src/utils/utils.ts b/packages/token-generation-readmodel-checker/src/utils/utils.ts index dd8f3b2080..ccab0bb44d 100644 --- a/packages/token-generation-readmodel-checker/src/utils/utils.ts +++ b/packages/token-generation-readmodel-checker/src/utils/utils.ts @@ -206,9 +206,7 @@ export async function compareTokenGenerationReadModel( const parsedAgreement = PlatformStatesAgreementEntry.safeParse(e); if (parsedAgreement.success) { acc.agreements.set( - unsafeBrandId( - getIdFromPlatformStatesPK(parsedAgreement.data.PK) - ), + parsedAgreement.data.agreementId, parsedAgreement.data ); return acc; @@ -445,13 +443,13 @@ export async function compareReadModelAgreementsWithPlatformStates({ if (platformStatesEntry && agreement) { const expectedPlatformStatesAgreementEntry: ComparisonPlatformStatesAgreementEntry = { - PK: makePlatformStatesAgreementPK(agreement.id), - state: agreementStateToItemState(agreement.state), - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ + PK: makePlatformStatesAgreementPK({ consumerId: agreement.consumerId, eserviceId: agreement.eserviceId, }), - GSISK_agreementTimestamp: extractAgreementTimestamp(agreement), + state: agreementStateToItemState(agreement.state), + agreementId: agreement.id, + agreementTimestamp: extractAgreementTimestamp(agreement), agreementDescriptorId: agreement.descriptorId, }; diff --git a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts index eb0186da8f..44f7a4cad6 100644 --- a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts +++ b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts @@ -148,18 +148,18 @@ describe("Token Generation Read Model Checker tests", () => { dynamoDBClient ); - const agreementEntryPK = makePlatformStatesAgreementPK(agreement.id); + const agreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { PK: agreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: generateId(), - }), - GSISK_agreementTimestamp: + agreementId: agreement.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement.stamps.activation!.when.toISOString(), - agreementDescriptorId: agreement.descriptorId, + agreementDescriptorId: generateId(), version: 1, updatedAt: new Date().toISOString(), }; @@ -463,15 +463,15 @@ describe("Token Generation Read Model Checker tests", () => { await addOneAgreement(agreement2); // platform-states - const agreementEntryPK1 = makePlatformStatesAgreementPK(agreement1.id); + const agreementEntryPK1 = makePlatformStatesAgreementPK({ + consumerId: agreement1.consumerId, + eserviceId: agreement1.eserviceId, + }); const platformStatesAgreementEntry1: PlatformStatesAgreementEntry = { PK: agreementEntryPK1, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement1.consumerId, - eserviceId: agreement1.eserviceId, - }), - GSISK_agreementTimestamp: + agreementId: agreement1.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, @@ -483,15 +483,15 @@ describe("Token Generation Read Model Checker tests", () => { dynamoDBClient ); - const agreementEntryPK2 = makePlatformStatesAgreementPK(agreement2.id); + const agreementEntryPK2 = makePlatformStatesAgreementPK({ + consumerId: agreement2.consumerId, + eserviceId: agreement2.eserviceId, + }); const platformStatesAgreementEntry2: PlatformStatesAgreementEntry = { PK: agreementEntryPK2, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement2.consumerId, - eserviceId: agreement2.eserviceId, - }), - GSISK_agreementTimestamp: + agreementId: agreement2.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement2.descriptorId, @@ -545,15 +545,15 @@ describe("Token Generation Read Model Checker tests", () => { await addOneAgreement(agreement2); // platform-states - const agreementEntryPK1 = makePlatformStatesAgreementPK(agreement1.id); + const agreementEntryPK1 = makePlatformStatesAgreementPK({ + consumerId: agreement1.consumerId, + eserviceId: agreement1.eserviceId, + }); const platformStatesAgreementEntry1: PlatformStatesAgreementEntry = { PK: agreementEntryPK1, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement1.consumerId, - eserviceId: agreement1.eserviceId, - }), - GSISK_agreementTimestamp: + agreementId: agreement1.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: generateId(), @@ -565,15 +565,15 @@ describe("Token Generation Read Model Checker tests", () => { dynamoDBClient ); - const agreementEntryPK2 = makePlatformStatesAgreementPK(agreement2.id); + const agreementEntryPK2 = makePlatformStatesAgreementPK({ + consumerId: agreement2.consumerId, + eserviceId: agreement2.eserviceId, + }); const platformStatesAgreementEntry2: PlatformStatesAgreementEntry = { PK: agreementEntryPK2, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), - }), - GSISK_agreementTimestamp: + agreementId: agreement2.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: generateId(), @@ -660,15 +660,15 @@ describe("Token Generation Read Model Checker tests", () => { }; // platform-states - const agreementEntryPK = makePlatformStatesAgreementPK(agreement.id); + const agreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { PK: agreementEntryPK, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement.consumerId, - eserviceId: agreement.eserviceId, - }), - GSISK_agreementTimestamp: + agreementId: agreement.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement.descriptorId, diff --git a/packages/token-generation-readmodel-checker/test/utils.test.ts b/packages/token-generation-readmodel-checker/test/utils.test.ts index 07c6e9c0ff..fa48c3bdfb 100644 --- a/packages/token-generation-readmodel-checker/test/utils.test.ts +++ b/packages/token-generation-readmodel-checker/test/utils.test.ts @@ -148,17 +148,15 @@ describe("Token Generation Read Model Checker utils tests", () => { await addOneAgreement(agreement2); // platform-states - const agreementEntryPrimaryKey1 = makePlatformStatesAgreementPK( - agreement1.id - ); + const agreementEntryPrimaryKey1 = makePlatformStatesAgreementPK({ + consumerId: agreement1.consumerId, + eserviceId: agreement1.eserviceId, + }); const platformAgreementEntry1: PlatformStatesAgreementEntry = { PK: agreementEntryPrimaryKey1, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: agreement1.consumerId, - eserviceId: agreement1.eserviceId, - }), - GSISK_agreementTimestamp: + agreementId: agreement1.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement1.stamps.activation!.when.toISOString(), agreementDescriptorId: agreement1.descriptorId, @@ -166,17 +164,15 @@ describe("Token Generation Read Model Checker utils tests", () => { updatedAt: new Date().toISOString(), }; - const agreementEntryPrimaryKey2 = makePlatformStatesAgreementPK( - agreement2.id - ); + const agreementEntryPrimaryKey2 = makePlatformStatesAgreementPK({ + consumerId: agreement2.consumerId, + eserviceId: agreement2.eserviceId, + }); const platformAgreementEntry2: PlatformStatesAgreementEntry = { PK: agreementEntryPrimaryKey2, state: itemState.active, - GSIPK_consumerId_eserviceId: makeGSIPKConsumerIdEServiceId({ - consumerId: generateId(), - eserviceId: generateId(), - }), - GSISK_agreementTimestamp: + agreementId: agreement2.id, + agreementTimestamp: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion agreement2.stamps.activation!.when.toISOString(), agreementDescriptorId: generateId(), From 2b59ce0a21e9695b11db23ceedaccccac6e8db97 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:55:10 +0100 Subject: [PATCH 124/126] Add process exit to token-generation-readmodel-checker (#1375) --- packages/token-generation-readmodel-checker/package.json | 2 +- packages/token-generation-readmodel-checker/src/index.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/token-generation-readmodel-checker/package.json b/packages/token-generation-readmodel-checker/package.json index a3bdd24652..bdc316542b 100644 --- a/packages/token-generation-readmodel-checker/package.json +++ b/packages/token-generation-readmodel-checker/package.json @@ -11,7 +11,7 @@ "lint:autofix": "eslint . --ext .ts,.tsx --fix", "format:check": "prettier --check src", "format:write": "prettier --write src", - "start": "tsx -r 'dotenv-flow/config' --watch ./src/index.ts", + "start": "tsx -r 'dotenv-flow/config' ./src/index.ts", "build": "tsc", "check": "tsc --project tsconfig.check.json" }, diff --git a/packages/token-generation-readmodel-checker/src/index.ts b/packages/token-generation-readmodel-checker/src/index.ts index d6f6816ca8..21d24d6063 100644 --- a/packages/token-generation-readmodel-checker/src/index.ts +++ b/packages/token-generation-readmodel-checker/src/index.ts @@ -23,3 +23,5 @@ async function main(): Promise { } await main(); + +process.exit(0); From 429ccb336d3c0b7ad4620ab2fe1ba5ba08a3efd7 Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:33:21 +0100 Subject: [PATCH 125/126] Fix archiving in token-generation-readmodel-checker (#1379) Co-authored-by: Roberto Taglioni --- .../src/utils/utils.ts | 79 ++++++----- .../tokenGenerationReadModelChecker.test.ts | 126 ++++++++++++++++++ 2 files changed, 173 insertions(+), 32 deletions(-) diff --git a/packages/token-generation-readmodel-checker/src/utils/utils.ts b/packages/token-generation-readmodel-checker/src/utils/utils.ts index ccab0bb44d..30c0507909 100644 --- a/packages/token-generation-readmodel-checker/src/utils/utils.ts +++ b/packages/token-generation-readmodel-checker/src/utils/utils.ts @@ -79,11 +79,9 @@ export function getLastPurposeVersion( export function getPurposeStateFromPurposeVersion( purposeVersion: PurposeVersion ): ItemState { - if (purposeVersion.state === purposeVersionState.active) { - return itemState.active; - } else { - return itemState.inactive; - } + return purposeVersion.state === purposeVersionState.active + ? itemState.active + : itemState.inactive; } export function getLastAgreement(agreements: Agreement[]): Agreement { @@ -369,6 +367,14 @@ export async function compareReadModelPurposesWithPlatformStates({ } if (platformStatesEntry && lastPurposeVersion) { + if (lastPurposeVersion.state === purposeVersionState.archived) { + logger.error( + `platform-states entry with ${platformStatesEntry.PK} should not be in the table because the purpose is archived` + ); + differencesCount++; + continue; + } + const expectedPlatformStatesPurposeEntry: ComparisonPlatformStatesPurposeEntry = { PK: makePlatformStatesPurposePK(purpose.id), @@ -399,6 +405,7 @@ export async function compareReadModelPurposesWithPlatformStates({ } // agreements +// eslint-disable-next-line sonarjs/cognitive-complexity export async function compareReadModelAgreementsWithPlatformStates({ platformStatesAgreementById, agreementsById, @@ -441,6 +448,14 @@ export async function compareReadModelAgreementsWithPlatformStates({ } if (platformStatesEntry && agreement) { + if (agreement.state === agreementState.archived) { + logger.error( + `platform-states entry with ${platformStatesEntry.PK} and agreementId ${agreement.id} should not be in the table because the agreement is archived` + ); + differencesCount++; + continue; + } + const expectedPlatformStatesAgreementEntry: ComparisonPlatformStatesAgreementEntry = { PK: makePlatformStatesAgreementPK({ @@ -499,25 +514,22 @@ export async function compareReadModelEServicesWithPlatformStates({ differencesCount++; logger.error(`Read model e-service not found for id: ${id}`); } else { - // Descriptors with a state other than deprecated, published or suspended are not considered because they are not expected in the platform-states - if ( - !eservice.descriptors.some( - (d) => - d.state === descriptorState.deprecated || - d.state === descriptorState.published || - d.state === descriptorState.suspended - ) - ) { - continue; - } - + // Descriptors with a state other than deprecated, published or suspended are not considered because they are not expected to be in the platform-states + const shouldPlatformStatesCatalogEntriesExist = eservice.descriptors.some( + (d) => + d.state === descriptorState.deprecated || + d.state === descriptorState.published || + d.state === descriptorState.suspended + ); const platformStatesEntries = platformStatesEServiceById.get(id); if (!platformStatesEntries) { - logger.error( - `platform-states entries not found for e-service with id: ${id}` - ); - differencesCount++; + if (shouldPlatformStatesCatalogEntriesExist) { + logger.error( + `platform-states entries not found for e-service with id: ${id}` + ); + differencesCount++; + } continue; } @@ -527,27 +539,30 @@ export async function compareReadModelEServicesWithPlatformStates({ >(); eservice.descriptors.forEach((descriptor) => { - expectedDescriptorsMap.set(descriptor.id, { - PK: makePlatformStatesEServiceDescriptorPK({ - eserviceId: eservice.id, - descriptorId: descriptor.id, - }), - state: descriptorStateToItemState(descriptor.state), - descriptorAudience: descriptor.audience, - descriptorVoucherLifespan: descriptor.voucherLifespan, - }); + if (shouldPlatformStatesCatalogEntriesExist) { + expectedDescriptorsMap.set(descriptor.id, { + PK: makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }), + state: descriptorStateToItemState(descriptor.state), + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + }); + } }); const allDescriptorIds = new Set([ ...expectedDescriptorsMap.keys(), ...platformStatesEntries.keys(), ]); - for (const descriptorId of allDescriptorIds) { const readModelEntry = expectedDescriptorsMap.get(descriptorId); const platformStatesEntry = platformStatesEntries.get(descriptorId); if (platformStatesEntry && !readModelEntry) { - logger.error(`Read model e-service not found for id: ${id}`); + logger.error( + `platform-states entry with ${platformStatesEntry.PK} should not be in the table because the descriptor state is not published, suspended or deprecated` + ); differencesCount++; } diff --git a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts index 44f7a4cad6..8ccf54b9f3 100644 --- a/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts +++ b/packages/token-generation-readmodel-checker/test/tokenGenerationReadModelChecker.test.ts @@ -370,6 +370,40 @@ describe("Token Generation Read Model Checker tests", () => { expect(purposeDifferences).toEqual(expectedDifferencesLength); }); + it("should detect differences when there's a platform-states purpose entry and the purpose is archived", async () => { + const purpose = getMockPurpose([ + getMockPurposeVersion(purposeVersionState.archived), + ]); + await addOnePurpose(purpose); + + // platform-states + const purposeEntryPK = makePlatformStatesPurposePK(purpose.id); + const platformStatesPurposeEntry: PlatformStatesPurposeEntry = { + PK: purposeEntryPK, + state: itemState.inactive, + purposeVersionId: purpose.versions[0].id, + purposeEserviceId: purpose.eserviceId, + purposeConsumerId: purpose.consumerId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformPurposeEntry( + platformStatesPurposeEntry, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const purposeDifferences = + await compareReadModelPurposesWithPlatformStates({ + platformStatesPurposeById: new Map([ + [purpose.id, platformStatesPurposeEntry], + ]), + purposesById: new Map([[purpose.id, purpose]]), + logger: genericLogger, + }); + expect(purposeDifferences).toEqual(expectedDifferencesLength); + }); + it("should detect differences when the platform-states entry is missing and the purpose is not archived", async () => { const purpose = getMockPurpose([ getMockPurposeVersion(purposeVersionState.active), @@ -601,6 +635,52 @@ describe("Token Generation Read Model Checker tests", () => { expect(agreementDifferences).toEqual(expectedDifferencesLength); }); + it("should detect differences when there's a platform-states agreement entry and the agreement is archived", async () => { + const agreement: Agreement = { + ...getMockAgreement(), + state: agreementState.archived, + stamps: { + activation: { + when: new Date(), + who: generateId(), + }, + }, + }; + await addOneAgreement(agreement); + + // platform-states + const agreementEntryPK = makePlatformStatesAgreementPK({ + consumerId: agreement.consumerId, + eserviceId: agreement.eserviceId, + }); + const platformStatesAgreementEntry: PlatformStatesAgreementEntry = { + PK: agreementEntryPK, + state: itemState.inactive, + agreementId: agreement.id, + agreementTimestamp: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + agreement.stamps.activation!.when.toISOString(), + agreementDescriptorId: agreement.descriptorId, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformAgreementEntry( + platformStatesAgreementEntry, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const agreementDifferences = + await compareReadModelAgreementsWithPlatformStates({ + platformStatesAgreementById: new Map([ + [agreement.id, platformStatesAgreementEntry], + ]), + agreementsById: new Map([[agreement.id, agreement]]), + logger: genericLogger, + }); + expect(agreementDifferences).toEqual(expectedDifferencesLength); + }); + it("should detect differences when the platform-states entry is missing and the agreement is not archived", async () => { const agreement: Agreement = { ...getMockAgreement(), @@ -879,6 +959,52 @@ describe("Token Generation Read Model Checker tests", () => { expect(catalogDifferences).toEqual(expectedDifferencesLength); }); + it("should detect differences when there's a platform-states catalog entry and the descriptor is not published, deprecated or suspended", async () => { + const descriptor: Descriptor = { + ...getMockDescriptor(), + state: descriptorState.archived, + audience: ["pagopa.it"], + }; + + const eservice: EService = { + ...getMockEService(), + descriptors: [descriptor], + }; + await addOneEService(eservice); + + // platform-states + const catalogEntryPK = makePlatformStatesEServiceDescriptorPK({ + eserviceId: eservice.id, + descriptorId: descriptor.id, + }); + const platformStatesCatalogEntry: PlatformStatesCatalogEntry = { + PK: catalogEntryPK, + state: itemState.inactive, + descriptorAudience: descriptor.audience, + descriptorVoucherLifespan: descriptor.voucherLifespan, + version: 1, + updatedAt: new Date().toISOString(), + }; + await writePlatformCatalogEntry( + platformStatesCatalogEntry, + dynamoDBClient + ); + + const expectedDifferencesLength = 1; + const catalogDifferences = + await compareReadModelEServicesWithPlatformStates({ + platformStatesEServiceById: new Map([ + [ + eservice.id, + new Map([[descriptor.id, platformStatesCatalogEntry]]), + ], + ]), + eservicesById: new Map([[eservice.id, eservice]]), + logger: genericLogger, + }); + expect(catalogDifferences).toEqual(expectedDifferencesLength); + }); + it("should detect differences when the platform-states entry is missing and the descriptor is not archived", async () => { const descriptor: Descriptor = { ...getMockDescriptor(), From c0ddea03918c313418c6243004b809e84ad20f3e Mon Sep 17 00:00:00 2001 From: shuyec <76391491+shuyec@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:44:57 +0100 Subject: [PATCH 126/126] Update authorization-server routes (#1387) --- .../open-api/authorizationServerApi.yml | 4 +- .../src/routers/AuthorizationServerRouter.ts | 127 +++++++++--------- .../src/routers/HealthRouter.ts | 4 +- 3 files changed, 70 insertions(+), 65 deletions(-) diff --git a/packages/api-clients/open-api/authorizationServerApi.yml b/packages/api-clients/open-api/authorizationServerApi.yml index 3fcbf37b1e..f6de8d7534 100644 --- a/packages/api-clients/open-api/authorizationServerApi.yml +++ b/packages/api-clients/open-api/authorizationServerApi.yml @@ -25,7 +25,7 @@ tags: description: Find out more url: http://swagger.io paths: - "/token.oauth2": + "/authorization-server/token.oauth2": post: tags: - auth @@ -96,7 +96,7 @@ paths: schema: type: integer description: Time interval in milliseconds. Allowed requests will be constantly replenished during the interval. At the end of the interval the max allowed requests will be available - /status: + /authorization-server/status: get: security: [] summary: Returns the application status diff --git a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts index 044a2df8a6..db1bf49fef 100644 --- a/packages/authorization-server/src/routers/AuthorizationServerRouter.ts +++ b/packages/authorization-server/src/routers/AuthorizationServerRouter.ts @@ -58,79 +58,82 @@ const authorizationServerRouter = ( validationErrorHandler: zodiosValidationErrorToApiProblem, } ); - authorizationServerRouter.post("/token.oauth2", async (req, res) => { - const ctx = fromAppContext(req.ctx); + authorizationServerRouter.post( + "/authorization-server/token.oauth2", + async (req, res) => { + const ctx = fromAppContext(req.ctx); - try { - const tokenResult = await tokenService.generateToken( - req.body, - ctx.correlationId, - ctx.logger - ); + try { + const tokenResult = await tokenService.generateToken( + req.body, + ctx.correlationId, + ctx.logger + ); + + const headers = rateLimiterHeadersFromStatus( + tokenResult.rateLimiterStatus + ); + res.set(headers); + + if (tokenResult.limitReached) { + const errorRes = makeApiProblem( + tooManyRequestsError(tokenResult.rateLimitedTenantId), + authorizationServerErrorMapper, + ctx.logger, + ctx.correlationId + ); - const headers = rateLimiterHeadersFromStatus( - tokenResult.rateLimiterStatus - ); - res.set(headers); + return res.status(errorRes.status).send(errorRes); + } - if (tokenResult.limitReached) { + return res.status(200).send({ + access_token: tokenResult.token.serialized, + token_type: "Bearer", + expires_in: tokenResult.token.payload.exp, + }); + } catch (err) { const errorRes = makeApiProblem( - tooManyRequestsError(tokenResult.rateLimitedTenantId), + err, authorizationServerErrorMapper, ctx.logger, ctx.correlationId ); + if (errorRes.status === constants.HTTP_STATUS_BAD_REQUEST) { + const cleanedError: Problem = { + title: "The request contains bad syntax or cannot be fulfilled.", + type: "about:blank", + status: constants.HTTP_STATUS_BAD_REQUEST, + detail: "Bad request", + errors: [ + { + code: "015-0008", + detail: "Unable to generate a token for the given request", + }, + ], + correlationId: ctx.correlationId, + }; - return res.status(errorRes.status).send(errorRes); - } - - return res.status(200).send({ - access_token: tokenResult.token.serialized, - token_type: "Bearer", - expires_in: tokenResult.token.payload.exp, - }); - } catch (err) { - const errorRes = makeApiProblem( - err, - authorizationServerErrorMapper, - ctx.logger, - ctx.correlationId - ); - if (errorRes.status === constants.HTTP_STATUS_BAD_REQUEST) { - const cleanedError: Problem = { - title: "The request contains bad syntax or cannot be fulfilled.", - type: "about:blank", - status: constants.HTTP_STATUS_BAD_REQUEST, - detail: "Bad request", - errors: [ - { - code: "015-0008", - detail: "Unable to generate a token for the given request", - }, - ], - correlationId: ctx.correlationId, - }; - - return res.status(cleanedError.status).send(cleanedError); - } else { - const cleanedError: Problem = { - title: "The request couldn't be fulfilled due to an internal error", - type: "internalServerError", - status: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, - detail: "Internal server error", - errors: [ - { - code: "015-0000", - detail: - "Unable to generate a token for the given request due to an internal error", - }, - ], - correlationId: ctx.correlationId, - }; - return res.status(cleanedError.status).send(cleanedError); + return res.status(cleanedError.status).send(cleanedError); + } else { + const cleanedError: Problem = { + title: "The request couldn't be fulfilled due to an internal error", + type: "internalServerError", + status: constants.HTTP_STATUS_INTERNAL_SERVER_ERROR, + detail: "Internal server error", + errors: [ + { + code: "015-0000", + detail: + "Unable to generate a token for the given request due to an internal error", + }, + ], + correlationId: ctx.correlationId, + }; + return res.status(cleanedError.status).send(cleanedError); + } } } - }); + ); return authorizationServerRouter; }; diff --git a/packages/authorization-server/src/routers/HealthRouter.ts b/packages/authorization-server/src/routers/HealthRouter.ts index 6658f9ab36..a30303414a 100644 --- a/packages/authorization-server/src/routers/HealthRouter.ts +++ b/packages/authorization-server/src/routers/HealthRouter.ts @@ -3,6 +3,8 @@ import { authorizationServerApi } from "pagopa-interop-api-clients"; const healthRouter = zodiosRouter(authorizationServerApi.healthApi.api); -healthRouter.get("/status", async (_, res) => res.status(200).send()); +healthRouter.get("/authorization-server/status", async (_, res) => + res.status(200).send() +); export default healthRouter;

)l4iJW(o?;Sbk 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 OVB-vw9F{DiM*j(2qHrt# literal 0 HcmV?d00001 diff --git a/packages/delegation-process/src/resources/assets/font/Montserrat-SemiBold.ttf b/packages/delegation-process/src/resources/assets/font/Montserrat-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ccaba1a86ca13fa394bfc3fa511bc5be0a20bb18 GIT binary patch literal 198720 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