diff --git a/infrastructure/terraform/modules/backend-api/module_delete_routing_config_lambda.tf b/infrastructure/terraform/modules/backend-api/module_delete_routing_config_lambda.tf index bb6ad4381..19919d025 100644 --- a/infrastructure/terraform/modules/backend-api/module_delete_routing_config_lambda.tf +++ b/infrastructure/terraform/modules/backend-api/module_delete_routing_config_lambda.tf @@ -65,4 +65,15 @@ data "aws_iam_policy_document" "delete_routing_config_lambda_policy" { var.kms_key_arn ] } + + statement { + sid = "AllowSSMParameterRead" + effect = "Allow" + + actions = [ + "ssm:GetParameter", + ] + + resources = [local.client_ssm_path_pattern] + } } diff --git a/infrastructure/terraform/modules/backend-api/module_submit_routing_config_lambda.tf b/infrastructure/terraform/modules/backend-api/module_submit_routing_config_lambda.tf index 599bc693d..c623bbac3 100644 --- a/infrastructure/terraform/modules/backend-api/module_submit_routing_config_lambda.tf +++ b/infrastructure/terraform/modules/backend-api/module_submit_routing_config_lambda.tf @@ -65,4 +65,15 @@ data "aws_iam_policy_document" "submit_routing_config_lambda_policy" { var.kms_key_arn ] } + + statement { + sid = "AllowSSMParameterRead" + effect = "Allow" + + actions = [ + "ssm:GetParameter", + ] + + resources = [local.client_ssm_path_pattern] + } } diff --git a/lambdas/backend-api/src/__tests__/templates/app/routing-config-client.test.ts b/lambdas/backend-api/src/__tests__/templates/app/routing-config-client.test.ts index d50f34b8c..3fff08b5d 100644 --- a/lambdas/backend-api/src/__tests__/templates/app/routing-config-client.test.ts +++ b/lambdas/backend-api/src/__tests__/templates/app/routing-config-client.test.ts @@ -275,7 +275,7 @@ describe('RoutingConfigClient', () => { }; mocks.clientConfigRepository.get.mockResolvedValueOnce({ - data: { features: {}, campaignIds: [campaignId] }, + data: { features: { routing: true }, campaignIds: [campaignId] }, }); mocks.routingConfigRepository.create.mockResolvedValueOnce({ @@ -298,9 +298,16 @@ describe('RoutingConfigClient', () => { }); }); - test('returns 400 error when input is invalid', async () => { + test('returns validation error when input is invalid', async () => { const { client, mocks } = setup(); + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { + features: { routing: true }, + campaignIds: [routingConfig.campaignId], + }, + }); + const result = await client.createRoutingConfig( { a: 1 } as unknown as CreateUpdateRoutingConfig, user @@ -356,7 +363,7 @@ describe('RoutingConfigClient', () => { }; mocks.clientConfigRepository.get.mockResolvedValueOnce({ - data: { features: {}, campaignIds: ['campaign'] }, + data: { features: { routing: true }, campaignIds: ['campaign'] }, }); mocks.routingConfigRepository.create.mockResolvedValueOnce({ @@ -420,6 +427,41 @@ describe('RoutingConfigClient', () => { }); }); + test('returns failure if routing feature is disabled for the client', async () => { + const { client, mocks } = setup(); + + const input: CreateUpdateRoutingConfig = { + name: 'rc', + campaignId: 'campaign', + cascade: [ + { + cascadeGroups: ['standard'], + channel: 'SMS', + channelType: 'primary', + defaultTemplateId: 'sms', + }, + ], + cascadeGroupOverrides: [{ name: 'standard' }], + }; + + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { features: { routing: false }, campaignIds: ['campaign'] }, + }); + + const result = await client.createRoutingConfig(input, user); + + expect(mocks.routingConfigRepository.create).not.toHaveBeenCalled(); + + expect(result).toEqual({ + error: { + errorMeta: { + code: 400, + description: 'Routing feature is disabled', + }, + }, + }); + }); + test('returns failure if campaignId is not allowed for the client', async () => { const { client, mocks } = setup(); @@ -438,7 +480,10 @@ describe('RoutingConfigClient', () => { }; mocks.clientConfigRepository.get.mockResolvedValueOnce({ - data: { features: {}, campaignIds: ['another campaign'] }, + data: { + features: { routing: true }, + campaignIds: ['another campaign'], + }, }); const result = await client.createRoutingConfig(input, user); @@ -460,6 +505,10 @@ describe('RoutingConfigClient', () => { test('returns completed routing config', async () => { const { client, mocks } = setup(); + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { features: { routing: true } }, + }); + const id = '2cb1c52d-befa-42f4-8628-06cfe63aa64d'; const completed: RoutingConfig = { @@ -482,12 +531,63 @@ describe('RoutingConfigClient', () => { data: completed, }); }); + + test('returns failures from client config repository', async () => { + const { client, mocks } = setup(); + + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + error: { + errorMeta: { + code: 500, + description: 'could not fetch client config', + }, + }, + }); + + const result = await client.submitRoutingConfig('some-id', user); + + expect(mocks.routingConfigRepository.submit).not.toHaveBeenCalled(); + + expect(result).toEqual({ + error: { + errorMeta: { + code: 500, + description: 'could not fetch client config', + }, + }, + }); + }); + + test('returns failure if routing feature is disabled for the client', async () => { + const { client, mocks } = setup(); + + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { features: { routing: false } }, + }); + + const result = await client.submitRoutingConfig('some-id', user); + + expect(mocks.routingConfigRepository.submit).not.toHaveBeenCalled(); + + expect(result).toEqual({ + error: { + errorMeta: { + code: 400, + description: 'Routing feature is disabled', + }, + }, + }); + }); }); describe('deleteRoutingConfig', () => { test('returns undefined after deleting routing config', async () => { const { client, mocks } = setup(); + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { features: { routing: true } }, + }); + const id = '2cb1c52d-befa-42f4-8628-06cfe63aa64d'; const deleted: RoutingConfig = { @@ -514,6 +614,10 @@ describe('RoutingConfigClient', () => { test('returns error response from repository', async () => { const { client, mocks } = setup(); + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { features: { routing: true } }, + }); + const id = '2cb1c52d-befa-42f4-8628-06cfe63aa64d'; const errorResponse = { @@ -531,6 +635,53 @@ describe('RoutingConfigClient', () => { expect(result).toEqual(errorResponse); }); + + test('returns failures from client config repository', async () => { + const { client, mocks } = setup(); + + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + error: { + errorMeta: { + code: 500, + description: 'could not fetch client config', + }, + }, + }); + + const result = await client.deleteRoutingConfig('some-id', user); + + expect(mocks.routingConfigRepository.delete).not.toHaveBeenCalled(); + + expect(result).toEqual({ + error: { + errorMeta: { + code: 500, + description: 'could not fetch client config', + }, + }, + }); + }); + + test('returns failure if routing feature is disabled for the client', async () => { + const { client, mocks } = setup(); + + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { features: { routing: false }, campaignIds: ['campaign'] }, + }); + + const result = await client.deleteRoutingConfig('some-id', user); + + expect(mocks.routingConfigRepository.delete).not.toHaveBeenCalled(); + + expect(result).toEqual({ + error: { + errorMeta: { + code: 400, + description: 'Routing feature is disabled', + }, + }, + }); + }); }); describe('updateRoutingConfig', () => { @@ -550,7 +701,10 @@ describe('RoutingConfigClient', () => { }; mocks.clientConfigRepository.get.mockResolvedValueOnce({ - data: { features: {}, campaignIds: [routingConfig.campaignId] }, + data: { + features: { routing: true }, + campaignIds: [routingConfig.campaignId], + }, }); mocks.routingConfigRepository.update.mockResolvedValueOnce({ @@ -576,6 +730,12 @@ describe('RoutingConfigClient', () => { test('returns validation error when update is invalid', async () => { const { client, mocks } = setup(); + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { + features: { routing: true }, + campaignIds: [routingConfig.campaignId], + }, + }); const update: CreateUpdateRoutingConfig = { campaignId: routingConfig.campaignId, @@ -648,6 +808,41 @@ describe('RoutingConfigClient', () => { }); }); + test('returns failure if routing feature is disabled for the client', async () => { + const { client, mocks } = setup(); + + const update: CreateUpdateRoutingConfig = { + cascade: routingConfig.cascade, + cascadeGroupOverrides: routingConfig.cascadeGroupOverrides, + name: routingConfig.name, + campaignId: 'this campaign', + }; + + mocks.clientConfigRepository.get.mockResolvedValueOnce({ + data: { + features: { routing: false }, + campaignIds: ['this campaign'], + }, + }); + + const result = await client.updateRoutingConfig( + routingConfig.id, + update, + user + ); + + expect(mocks.routingConfigRepository.update).not.toHaveBeenCalled(); + + expect(result).toEqual({ + error: { + errorMeta: { + code: 400, + description: 'Routing feature is disabled', + }, + }, + }); + }); + test('returns failure if campaignId is not allowed for the client', async () => { const { client, mocks } = setup(); @@ -659,7 +854,10 @@ describe('RoutingConfigClient', () => { }; mocks.clientConfigRepository.get.mockResolvedValueOnce({ - data: { features: {}, campaignIds: ['another campaign'] }, + data: { + features: { routing: true }, + campaignIds: ['another campaign'], + }, }); const result = await client.updateRoutingConfig( diff --git a/lambdas/backend-api/src/templates/app/routing-config-client.ts b/lambdas/backend-api/src/templates/app/routing-config-client.ts index b4eeb033c..5f8abf9f1 100644 --- a/lambdas/backend-api/src/templates/app/routing-config-client.ts +++ b/lambdas/backend-api/src/templates/app/routing-config-client.ts @@ -22,15 +22,6 @@ export class RoutingConfigClient { payload: unknown, user: User ): Promise> { - const validationResult = await validate( - $CreateUpdateRoutingConfig, - payload - ); - - if (validationResult.error) return validationResult; - - const validated = validationResult.data; - const clientConfigurationResult = await this.clientConfigRepository.get( user.clientId ); @@ -40,6 +31,22 @@ export class RoutingConfigClient { if (clientConfigurationError) return clientConfigurationResult; + if (!clientConfiguration?.features.routing) { + return failure( + ErrorCase.VALIDATION_FAILED, + 'Routing feature is disabled' + ); + } + + const validationResult = await validate( + $CreateUpdateRoutingConfig, + payload + ); + + if (validationResult.error) return validationResult; + + const validated = validationResult.data; + if (!clientConfiguration?.campaignIds?.includes(validated.campaignId)) { return failure( ErrorCase.VALIDATION_FAILED, @@ -55,6 +62,15 @@ export class RoutingConfigClient { payload: unknown, user: User ): Promise> { + const clientConfigurationResult = await this.clientConfigRepository.get( + user.clientId + ); + + const { data: clientConfiguration, error: clientConfigurationError } = + clientConfigurationResult; + + if (clientConfigurationError) return clientConfigurationResult; + const validationResult = await validate( $CreateUpdateRoutingConfig, payload @@ -64,14 +80,12 @@ export class RoutingConfigClient { const validated = validationResult.data; - const clientConfigurationResult = await this.clientConfigRepository.get( - user.clientId - ); - - const { data: clientConfiguration, error: clientConfigurationError } = - clientConfigurationResult; - - if (clientConfigurationError) return clientConfigurationResult; + if (!clientConfiguration?.features.routing) { + return failure( + ErrorCase.VALIDATION_FAILED, + 'Routing feature is disabled' + ); + } if (!clientConfiguration?.campaignIds?.includes(validated.campaignId)) { return failure( @@ -91,6 +105,22 @@ export class RoutingConfigClient { routingConfigId: string, user: User ): Promise> { + const clientConfigurationResult = await this.clientConfigRepository.get( + user.clientId + ); + + const { data: clientConfiguration, error: clientConfigurationError } = + clientConfigurationResult; + + if (clientConfigurationError) return clientConfigurationResult; + + if (!clientConfiguration?.features.routing) { + return failure( + ErrorCase.VALIDATION_FAILED, + 'Routing feature is disabled' + ); + } + return this.routingConfigRepository.submit(routingConfigId, user); } @@ -98,6 +128,22 @@ export class RoutingConfigClient { routingConfigId: string, user: User ): Promise> { + const clientConfigurationResult = await this.clientConfigRepository.get( + user.clientId + ); + + const { data: clientConfiguration, error: clientConfigurationError } = + clientConfigurationResult; + + if (clientConfigurationError) return clientConfigurationResult; + + if (!clientConfiguration?.features.routing) { + return failure( + ErrorCase.VALIDATION_FAILED, + 'Routing feature is disabled' + ); + } + const result = await this.routingConfigRepository.delete( routingConfigId, user diff --git a/package-lock.json b/package-lock.json index 526a68ef9..01e0da383 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19222,12 +19222,12 @@ "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@smithy/abort-controller": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.0.tgz", - "integrity": "sha512-PLUYa+SUKOEZtXFURBu/CNxlsxfaFGxSBPcStL13KpVeVWIfdezWyDqkz7iDLmwnxojXD0s5KzuB5HGHvt4Aeg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.2.tgz", + "integrity": "sha512-fPbcmEI+A6QiGOuumTpKSo7z+9VYr5DLN8d5/8jDJOwmt4HAKy/UGuRstCMpKbtr+FMaHH4pvFinSAbIAYCHZQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19260,15 +19260,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.0.tgz", - "integrity": "sha512-9oH+n8AVNiLPK/iK/agOsoWfrKZ3FGP3502tkksd6SRsKMYiu7AFX0YXo6YBADdsAj7C+G/aLKdsafIJHxuCkQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.3.2.tgz", + "integrity": "sha512-F/G+VaulIebINyfvcoXmODgIc7JU/lxWK9/iI0Divxyvd2QWB7/ZcF7JKwMssWI6/zZzlMkq/Pt6ow2AOEebPw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.2", + "@smithy/types": "^4.7.1", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-middleware": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -19276,18 +19276,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.15.0.tgz", - "integrity": "sha512-VJWncXgt+ExNn0U2+Y7UywuATtRYaodGQKFo9mDyh70q+fJGedfrqi2XuKU1BhiLeXgg6RZrW7VEKfeqFhHAJA==", + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.16.1.tgz", + "integrity": "sha512-yRx5ag3xEQ/yGvyo80FVukS7ZkeUP49Vbzg0MjfHLkuCIgg5lFtaEJfZR178KJmjWPqLU4d0P4k7SKgF9UkOaQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/middleware-serde": "^4.2.2", + "@smithy/protocol-http": "^5.3.2", + "@smithy/types": "^4.7.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/util-middleware": "^4.2.2", + "@smithy/util-stream": "^4.5.2", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" @@ -19297,15 +19297,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.0.tgz", - "integrity": "sha512-SOhFVvFH4D5HJZytb0bLKxCrSnwcqPiNlrw+S4ZXjMnsC+o9JcUQzbZOEQcA8yv9wJFNhfsUiIUKiEnYL68Big==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.2.tgz", + "integrity": "sha512-hOjFTK+4mfehDnfjNkPqHUKBKR2qmlix5gy7YzruNbTdeoBE3QkfNCPvuCK2r05VUJ02QQ9bz2G41CxhSexsMw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", + "@smithy/node-config-provider": "^4.3.2", + "@smithy/property-provider": "^4.2.2", + "@smithy/types": "^4.7.1", + "@smithy/url-parser": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -19383,14 +19383,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.1.tgz", - "integrity": "sha512-3AvYYbB+Dv5EPLqnJIAgYw/9+WzeBiUYS8B+rU0pHq5NMQMvrZmevUROS4V2GAt0jEOn9viBzPLrZE+riTNd5Q==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.3.tgz", + "integrity": "sha512-cipIcM3xQ5NdIVwcRb37LaQwIxZNMEZb/ZOPmLFS9uGo9TGx2dGCyMBj9oT7ypH4TUD/kOTc/qHmwQzthrSk+g==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.2", + "@smithy/querystring-builder": "^4.2.2", + "@smithy/types": "^4.7.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, @@ -19414,12 +19414,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.0.tgz", - "integrity": "sha512-ugv93gOhZGysTctZh9qdgng8B+xO0cj+zN0qAZ+Sgh7qTQGPOJbMdIuyP89KNfUyfAqFSNh5tMvC+h2uCpmTtA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.2.tgz", + "integrity": "sha512-xuOPGrF2GUP+9og5NU02fplRVjJjMhAaY8ZconB3eLKjv/VSV9/s+sFf72MYO5Q2jcSRVk/ywZHpyGbE3FYnFQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -19443,12 +19443,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.0.tgz", - "integrity": "sha512-ZmK5X5fUPAbtvRcUPtk28aqIClVhbfcmfoS4M7UQBTnDdrNxhsrxYVv0ZEl5NaPSyExsPWqL4GsPlRvtlwg+2A==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.2.tgz", + "integrity": "sha512-Z0844Zpoid5L1DmKX2+cn2Qu9i3XWjhzwYBRJEWrKJwjUuhEkzf37jKPj9dYFsZeKsAbS2qI0JyLsYafbXJvpA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19482,13 +19482,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.0.tgz", - "integrity": "sha512-6ZAnwrXFecrA4kIDOcz6aLBhU5ih2is2NdcZtobBDSdSHtE9a+MThB5uqyK4XXesdOCvOcbCm2IGB95birTSOQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.2.tgz", + "integrity": "sha512-aJ7LAuIXStF6EqzRVX9kAW+6/sYoJJv0QqoFrz2BhA9r/85kLYOJ6Ph47wYSGBxzSLxsYT5jqgMw/qpbv1+m+w==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19496,18 +19496,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.1.tgz", - "integrity": "sha512-JtM4SjEgImLEJVXdsbvWHYiJ9dtuKE8bqLlvkvGi96LbejDL6qnVpVxEFUximFodoQbg0Gnkyff9EKUhFhVJFw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.3.tgz", + "integrity": "sha512-CfxQ6X9L87/3C67Po6AGWXsx8iS4w2BO8vQEZJD6hwqg2vNRC/lMa2O5wXYCG9tKotdZ0R8KG33TS7kpUnYKiw==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/core": "^3.16.1", + "@smithy/middleware-serde": "^4.2.2", + "@smithy/node-config-provider": "^4.3.2", + "@smithy/shared-ini-file-loader": "^4.3.2", + "@smithy/types": "^4.7.1", + "@smithy/url-parser": "^4.2.2", + "@smithy/util-middleware": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -19515,18 +19515,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.1.tgz", - "integrity": "sha512-wXxS4ex8cJJteL0PPQmWYkNi9QKDWZIpsndr0wZI2EL+pSSvA/qqxXU60gBOJoIc2YgtZSWY/PE86qhKCCKP1w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.3.tgz", + "integrity": "sha512-EHnKGeFuzbmER4oSl/VJDxPLi+aiZUb3nk5KK8eNwHjMhI04jHlui2ZkaBzMfNmXOgymaS6zV//fyt6PSnI1ow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.2", + "@smithy/protocol-http": "^5.3.2", + "@smithy/service-error-classification": "^4.2.2", + "@smithy/smithy-client": "^4.8.1", + "@smithy/types": "^4.7.1", + "@smithy/util-middleware": "^4.2.2", + "@smithy/util-retry": "^4.2.2", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" }, @@ -19535,13 +19535,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.0.tgz", - "integrity": "sha512-rpTQ7D65/EAbC6VydXlxjvbifTf4IH+sADKg6JmAvhkflJO2NvDeyU9qsWUNBelJiQFcXKejUHWRSdmpJmEmiw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.2.tgz", + "integrity": "sha512-tDMPMBCsA1GBxanShhPvQYwdiau3NmctUp+eELMhUTDua+EUrugXlaKCnTMMoEB5mbHFebdv81uJPkVP02oihA==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19549,12 +19549,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.0.tgz", - "integrity": "sha512-G5CJ//eqRd9OARrQu9MK1H8fNm2sMtqFh6j8/rPozhEL+Dokpvi1Og+aCixTuwDAGZUkJPk6hJT5jchbk/WCyg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.2.tgz", + "integrity": "sha512-7rgzDyLOQouh1bC6gOXnCGSX2dqvbOclgClsFkj735xQM2CHV63Ams8odNZGJgcqnBsEz44V/pDGHU6ALEUD+w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19562,14 +19562,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.0.tgz", - "integrity": "sha512-5QgHNuWdT9j9GwMPPJCKxy2KDxZ3E5l4M3/5TatSZrqYVoEiqQrDfAq8I6KWZw7RZOHtVtCzEPdYz7rHZixwcA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.2.tgz", + "integrity": "sha512-u38G0Audi2ORsL0QnzhopZ3yweMblQf8CZNbzUJ3wfTtZ7OiOwOzee0Nge/3dKeG/8lx0kt8K0kqDi6sYu0oKQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.2", + "@smithy/shared-ini-file-loader": "^4.3.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19577,15 +19577,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.3.0.tgz", - "integrity": "sha512-RHZ/uWCmSNZ8cneoWEVsVwMZBKy/8123hEpm57vgGXA3Irf/Ja4v9TVshHK2ML5/IqzAZn0WhINHOP9xl+Qy6Q==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.1.tgz", + "integrity": "sha512-9gKJoL45MNyOCGTG082nmx0A6KrbLVQ+5QSSKyzRi0AzL0R81u3wC1+nPvKXgTaBdAKM73fFPdCBHpmtipQwdQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/abort-controller": "^4.2.2", + "@smithy/protocol-http": "^5.3.2", + "@smithy/querystring-builder": "^4.2.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19593,12 +19593,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.0.tgz", - "integrity": "sha512-rV6wFre0BU6n/tx2Ztn5LdvEdNZ2FasQbPQmDOPfV9QQyDmsCkOAB0osQjotRCQg+nSKFmINhyda0D3AnjSBJw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.2.tgz", + "integrity": "sha512-MW7MfI+qYe/Ue5RH0uEztEKB+vBlOMM+1Dz68qzTsY8fC9kanXMFPEVdiq35JTGKWt5wZAjU1R0uXYEjK2MM1g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19606,12 +19606,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.0.tgz", - "integrity": "sha512-6POSYlmDnsLKb7r1D3SVm7RaYW6H1vcNcTWGWrF7s9+2noNYvUsm7E4tz5ZQ9HXPmKn6Hb67pBDRIjrT4w/d7Q==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.2.tgz", + "integrity": "sha512-nkKOI8xEkBXUmdxsFExomOb+wkU+Xgn0Fq2LMC7YIX5r4YPUg7PLayV/s/u3AtbyjWYlrvN7nAiDTLlqSdUjHw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19619,12 +19619,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.0.tgz", - "integrity": "sha512-Q4oFD0ZmI8yJkiPPeGUITZj++4HHYCW3pYBYfIobUCkYpI6mbkzmG1MAQQ3lJYYWj3iNqfzOenUZu+jqdPQ16A==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.2.tgz", + "integrity": "sha512-YgXvq89o+R/8zIoeuXYv8Ysrbwgjx+iVYu9QbseqZjMDAhIg/FRt7jis0KASYFtd/Cnsnz4/nYTJXkJDWe8wHg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, @@ -19633,12 +19633,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.0.tgz", - "integrity": "sha512-BjATSNNyvVbQxOOlKse0b0pSezTWGMvA87SvoFoFlkRsKXVsN3bEtjCxvsNXJXfnAzlWFPaT9DmhWy1vn0sNEA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.2.tgz", + "integrity": "sha512-DczOD2yJy3NXcv1JvhjFC7bIb/tay6nnIRD/qrzBaju5lrkVBOwCT3Ps37tra20wy8PicZpworStK7ZcI9pCRQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19646,24 +19646,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.0.tgz", - "integrity": "sha512-Ylv1ttUeKatpR0wEOMnHf1hXMktPUMObDClSWl2TpCVT4DwtJhCeighLzSLbgH3jr5pBNM0LDXT5yYxUvZ9WpA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.2.tgz", + "integrity": "sha512-1X17cMLwe/vb4RpZbQVpJ1xQQ7fhQKggMdt3qjdV3+6QNllzvUXyS3WFnyaFWLyaGqfYHKkNONbO1fBCMQyZtQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0" + "@smithy/types": "^4.7.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.0.tgz", - "integrity": "sha512-VCUPPtNs+rKWlqqntX0CbVvWyjhmX30JCtzO+s5dlzzxrvSfRh5SY0yxnkirvc1c80vdKQttahL71a9EsdolSQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.2.tgz", + "integrity": "sha512-AWnLgSmOTdDXM8aZCN4Im0X07M3GGffeL9vGfea4mdKZD0cPT9yLF9SsRbEa00tHLI+KfubDrmjpaKT2pM4GdQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19671,16 +19671,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.0.tgz", - "integrity": "sha512-MKNyhXEs99xAZaFhm88h+3/V+tCRDQ+PrDzRqL0xdDpq4gjxcMmf5rBA3YXgqZqMZ/XwemZEurCBQMfxZOWq/g==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.2.tgz", + "integrity": "sha512-BRnQGGyaRSSL0KtjjFF9YoSSg8qzSqHMub4H2iKkd+LZNzZ1b7H5amslZBzi+AnvuwPMyeiNv0oqay/VmIuoRA==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.2", + "@smithy/types": "^4.7.1", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-middleware": "^4.2.2", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -19690,17 +19690,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.7.1.tgz", - "integrity": "sha512-WXVbiyNf/WOS/RHUoFMkJ6leEVpln5ojCjNBnzoZeMsnCg3A0BRhLK3WYc4V7PmYcYPZh9IYzzAg9XcNSzYxYQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.8.1.tgz", + "integrity": "sha512-N5wK57pVThzLVK5NgmHxocTy5auqGDGQ+JsL5RjCTriPt8JLYgXT0Awa915zCpzc9hXHDOKqDX5g9BFdwkSfUA==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-endpoint": "^4.3.1", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.5.0", + "@smithy/core": "^3.16.1", + "@smithy/middleware-endpoint": "^4.3.3", + "@smithy/middleware-stack": "^4.2.2", + "@smithy/protocol-http": "^5.3.2", + "@smithy/types": "^4.7.1", + "@smithy/util-stream": "^4.5.2", "tslib": "^2.6.2" }, "engines": { @@ -19708,9 +19708,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.6.0.tgz", - "integrity": "sha512-4lI9C8NzRPOv66FaY1LL1O/0v0aLVrq/mXP/keUa9mJOApEeae43LsLd2kZRUJw91gxOQfLIrV3OvqPgWz1YsA==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.7.1.tgz", + "integrity": "sha512-WwP7vzoDyzvIFLzF5UhLQ6AsEx/PvSObzlNtJNW3lLy+BaSvTqCU628QKVvcJI/dydlAS1mSHQP7anKcxDcOxA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -19720,13 +19720,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.0.tgz", - "integrity": "sha512-AlBmD6Idav2ugmoAL6UtR6ItS7jU5h5RNqLMZC7QrLCoITA9NzIN3nx9GWi8g4z1pfWh2r9r96SX/jHiNwPJ9A==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.2.tgz", + "integrity": "sha512-s2EYKukaswzjiHJCss6asB1F4zjRc0E/MFyceAKzb3+wqKA2Z/+Gfhb5FP8xVVRHBAvBkregaQAydifgbnUlCw==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/querystring-parser": "^4.2.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19797,14 +19797,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.0.tgz", - "integrity": "sha512-H4MAj8j8Yp19Mr7vVtGgi7noJjvjJbsKQJkvNnLlrIFduRFT5jq5Eri1k838YW7rN2g5FTnXpz5ktKVr1KVgPQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.2.tgz", + "integrity": "sha512-6JvKHZ5GORYkEZ2+yJKEHp6dQQKng+P/Mu3g3CDy0fRLQgXEO8be+FLrBGGb4kB9lCW6wcQDkN7kRiGkkVAXgg==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.2", + "@smithy/smithy-client": "^4.8.1", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19812,17 +19812,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.1.tgz", - "integrity": "sha512-PuDcgx7/qKEMzV1QFHJ7E4/MMeEjaA7+zS5UNcHCLPvvn59AeZQ0DSDGMpqC2xecfa/1cNGm4l8Ec/VxCuY7Ug==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.3.tgz", + "integrity": "sha512-bkTGuMmKvghfCh9NayADrQcjngoF8P+XTgID5r3rm+8LphFiuM6ERqpBS95YyVaLjDetnKus9zK/bGlkQOOtNQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.3.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/config-resolver": "^4.3.2", + "@smithy/credential-provider-imds": "^4.2.2", + "@smithy/node-config-provider": "^4.3.2", + "@smithy/property-provider": "^4.2.2", + "@smithy/smithy-client": "^4.8.1", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19830,13 +19830,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.0.tgz", - "integrity": "sha512-TXeCn22D56vvWr/5xPqALc9oO+LN+QpFjrSM7peG/ckqEPoI3zaKZFp+bFwfmiHhn5MGWPaLCqDOJPPIixk9Wg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.2.tgz", + "integrity": "sha512-ZQi6fFTMBkfwwSPAlcGzArmNILz33QH99CL8jDfVWrzwVVcZc56Mge10jGk0zdRgWPXyL1/OXKjfw4vT5VtRQg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19856,12 +19856,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.0.tgz", - "integrity": "sha512-u9OOfDa43MjagtJZ8AapJcmimP+K2Z7szXn8xbty4aza+7P1wjFmy2ewjSbhEiYQoW1unTlOAIV165weYAaowA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.2.tgz", + "integrity": "sha512-wL9tZwWKy0x0qf6ffN7tX5CT03hb1e7XpjdepaKfKcPcyn5+jHAWPqivhF1Sw/T5DYi9wGcxsX8Lu07MOp2Puw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19869,13 +19869,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.0.tgz", - "integrity": "sha512-BWSiuGbwRnEE2SFfaAZEX0TqaxtvtSYPM/J73PFVm+A29Fg1HTPiYFb8TmX1DXp4hgcdyJcNQmprfd5foeORsg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.2.tgz", + "integrity": "sha512-TlbnWAOoCuG2PgY0Hi3BGU1w2IXs3xDsD4E8WDfKRZUn2qx3wRA9mbYnmpWHPswTJCz2L+ebh+9OvD42sV4mNw==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/service-error-classification": "^4.2.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { @@ -19883,14 +19883,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.0.tgz", - "integrity": "sha512-0TD5M5HCGu5diEvZ/O/WquSjhJPasqv7trjoqHyWjNh/FBeBl7a0ztl9uFMOsauYtRfd8jvpzIAQhDHbx+nvZw==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.2.tgz", + "integrity": "sha512-RWYVuQVKtNbr7E0IxV8XHDId714yHPTxU6dHScd6wSMWAXboErzTG7+xqcL+K3r0Xg0cZSlfuNhl1J0rzMLSSw==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.1", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/fetch-http-handler": "^5.3.3", + "@smithy/node-http-handler": "^4.4.1", + "@smithy/types": "^4.7.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", @@ -19927,13 +19927,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.0.tgz", - "integrity": "sha512-0Z+nxUU4/4T+SL8BCNN4ztKdQjToNvUYmkF1kXO5T7Yz3Gafzh0HeIG6mrkN8Fz3gn9hSyxuAT+6h4vM+iQSBQ==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.2.tgz", + "integrity": "sha512-ZkanmAo9F47PIxuxaQ1E+VPn/jNIbOM7cpJyABfyI15jnr4l5toSDVXPRuvHIyC2f4fMYC7EKe5DIde7YP7c7A==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/abort-controller": "^4.2.2", + "@smithy/types": "^4.7.1", "tslib": "^2.6.2" }, "engines": { diff --git a/tests/test-team/helpers/auth/cognito-auth-helper.ts b/tests/test-team/helpers/auth/cognito-auth-helper.ts index e2ff085f8..edb73b477 100644 --- a/tests/test-team/helpers/auth/cognito-auth-helper.ts +++ b/tests/test-team/helpers/auth/cognito-auth-helper.ts @@ -122,6 +122,14 @@ export const testUsers: Record = { userId: 'UserWithFallbackCampaignId', clientKey: 'ClientWithFallbackCampaignId', }, + + /** + * UserRoutingEnabled belongs to an alternate client with routing enabled + */ + UserRoutingEnabled: { + userId: 'UserWithRoutingEnabled', + clientKey: 'ClientRoutingEnabled', + }, }; export type TestUser = TestUserStaticDetails & diff --git a/tests/test-team/helpers/client/client-helper.ts b/tests/test-team/helpers/client/client-helper.ts index a8c8fa8f8..2fd9a76e3 100644 --- a/tests/test-team/helpers/client/client-helper.ts +++ b/tests/test-team/helpers/client/client-helper.ts @@ -15,7 +15,7 @@ export type ClientConfiguration = { }; export type ClientKey = - `Client${1 | 2 | 3 | 4 | 5 | 6 | 'WithMultipleCampaigns' | 'WithFallbackCampaignId'}`; + `Client${1 | 2 | 3 | 4 | 5 | 6 | 'WithMultipleCampaigns' | 'WithFallbackCampaignId' | 'RoutingEnabled'}`; type TestClients = Record; @@ -96,6 +96,15 @@ export const testClients: TestClients = { routing: false, }, }, + + /** + * ClientRoutingEnabled is an alternative client with routing enabled + */ + ClientRoutingEnabled: { + campaignIds: ['RoutingEnabledCampaign'], + name: 'Routing Enabled Client', + features: { proofing: false, routing: true }, + }, }; export class ClientConfigurationHelper { diff --git a/tests/test-team/template-mgmt-api-tests/create-routing-configuration.api.spec.ts b/tests/test-team/template-mgmt-api-tests/create-routing-configuration.api.spec.ts index 8b3da0a5e..7e1e557a7 100644 --- a/tests/test-team/template-mgmt-api-tests/create-routing-configuration.api.spec.ts +++ b/tests/test-team/template-mgmt-api-tests/create-routing-configuration.api.spec.ts @@ -16,10 +16,12 @@ test.describe('POST /v1/routing-configuration', () => { const storageHelper = new RoutingConfigStorageHelper(); let user1: TestUser; let userSharedClient: TestUser; + let userRoutingDisabled: TestUser; test.beforeAll(async () => { user1 = await authHelper.getTestUser(testUsers.User1.userId); userSharedClient = await authHelper.getTestUser(testUsers.User7.userId); + userRoutingDisabled = await authHelper.getTestUser(testUsers.User2.userId); }); test.afterAll(async () => { @@ -229,4 +231,25 @@ test.describe('POST /v1/routing-configuration', () => { data: created.data, }); }); + + test('returns 400 if routing feature is disabled on the client', async ({ + request, + }) => { + const response = await request.post( + `${process.env.API_BASE_URL}/v1/routing-configuration`, + { + headers: { + Authorization: await userRoutingDisabled.getAccessToken(), + }, + data: RoutingConfigFactory.create(userRoutingDisabled).apiPayload, + } + ); + + expect(response.status()).toBe(400); + + expect(await response.json()).toEqual({ + statusCode: 400, + technicalMessage: 'Routing feature is disabled', + }); + }); }); diff --git a/tests/test-team/template-mgmt-api-tests/delete-routing-config.api.spec.ts b/tests/test-team/template-mgmt-api-tests/delete-routing-config.api.spec.ts index 57d8ae649..4942ccf6f 100644 --- a/tests/test-team/template-mgmt-api-tests/delete-routing-config.api.spec.ts +++ b/tests/test-team/template-mgmt-api-tests/delete-routing-config.api.spec.ts @@ -14,6 +14,7 @@ test.describe('DELETE /v1/routing-configuration/:routingConfigId', () => { let user1: TestUser; let userDifferentClient: TestUser; let userSharedClient: TestUser; + let userRoutingDisabled: TestUser; let routingConfigNoUpdates: FactoryRoutingConfig; let routingConfigSuccessfullyDelete: FactoryRoutingConfig; @@ -23,8 +24,11 @@ test.describe('DELETE /v1/routing-configuration/:routingConfigId', () => { test.beforeAll(async () => { user1 = await authHelper.getTestUser(testUsers.User1.userId); - userDifferentClient = await authHelper.getTestUser(testUsers.User2.userId); + userDifferentClient = await authHelper.getTestUser( + testUsers.UserRoutingEnabled.userId + ); userSharedClient = await authHelper.getTestUser(testUsers.User7.userId); + userRoutingDisabled = await authHelper.getTestUser(testUsers.User2.userId); routingConfigNoUpdates = RoutingConfigFactory.create(user1); routingConfigSuccessfullyDelete = RoutingConfigFactory.create(user1); @@ -171,4 +175,24 @@ test.describe('DELETE /v1/routing-configuration/:routingConfigId', () => { expect(response.status()).toBe(204); }); + + test('returns 400 if routing feature is disabled on the client', async ({ + request, + }) => { + const response = await request.delete( + `${process.env.API_BASE_URL}/v1/routing-configuration/some-routing-config`, + { + headers: { + Authorization: await userRoutingDisabled.getAccessToken(), + }, + } + ); + + expect(response.status()).toBe(400); + + expect(await response.json()).toEqual({ + statusCode: 400, + technicalMessage: 'Routing feature is disabled', + }); + }); }); diff --git a/tests/test-team/template-mgmt-api-tests/submit-routing-config.api.spec.ts b/tests/test-team/template-mgmt-api-tests/submit-routing-config.api.spec.ts index e40ff41a9..ee8743a78 100644 --- a/tests/test-team/template-mgmt-api-tests/submit-routing-config.api.spec.ts +++ b/tests/test-team/template-mgmt-api-tests/submit-routing-config.api.spec.ts @@ -16,6 +16,7 @@ test.describe('PATCH /v1/routing-configuration/:routingConfigId/submit', () => { let user1: TestUser; let userDifferentClient: TestUser; let userSharedClient: TestUser; + let userRoutingDisabled: TestUser; let routingConfigNoUpdates: FactoryRoutingConfig; let routingConfigSuccessfullySubmit: FactoryRoutingConfig; @@ -25,8 +26,11 @@ test.describe('PATCH /v1/routing-configuration/:routingConfigId/submit', () => { test.beforeAll(async () => { user1 = await authHelper.getTestUser(testUsers.User1.userId); - userDifferentClient = await authHelper.getTestUser(testUsers.User2.userId); + userDifferentClient = await authHelper.getTestUser( + testUsers.UserRoutingEnabled.userId + ); userSharedClient = await authHelper.getTestUser(testUsers.User7.userId); + userRoutingDisabled = await authHelper.getTestUser(testUsers.User2.userId); routingConfigNoUpdates = RoutingConfigFactory.create(user1); routingConfigSuccessfullySubmit = RoutingConfigFactory.create(user1); @@ -204,4 +208,24 @@ test.describe('PATCH /v1/routing-configuration/:routingConfigId/submit', () => { }, }); }); + + test('returns 400 if routing feature is disabled on the client', async ({ + request, + }) => { + const response = await request.patch( + `${process.env.API_BASE_URL}/v1/routing-configuration/some-routing-config/submit`, + { + headers: { + Authorization: await userRoutingDisabled.getAccessToken(), + }, + } + ); + + expect(response.status()).toBe(400); + + expect(await response.json()).toEqual({ + statusCode: 400, + technicalMessage: 'Routing feature is disabled', + }); + }); }); diff --git a/tests/test-team/template-mgmt-api-tests/update-routing-config.api.spec.ts b/tests/test-team/template-mgmt-api-tests/update-routing-config.api.spec.ts index 4ab669bfc..177f313b1 100644 --- a/tests/test-team/template-mgmt-api-tests/update-routing-config.api.spec.ts +++ b/tests/test-team/template-mgmt-api-tests/update-routing-config.api.spec.ts @@ -15,6 +15,7 @@ test.describe('PUT /v1/routing-configuration/:routingConfigId', () => { let user1: TestUser; let userDifferentClient: TestUser; let userSharedClient: TestUser; + let userRoutingDisabled: TestUser; let routingConfigNoUpdates: FactoryRoutingConfig; let routingConfigSuccessfullyUpdate: FactoryRoutingConfig; @@ -24,8 +25,11 @@ test.describe('PUT /v1/routing-configuration/:routingConfigId', () => { test.beforeAll(async () => { user1 = await authHelper.getTestUser(testUsers.User1.userId); - userDifferentClient = await authHelper.getTestUser(testUsers.User2.userId); + userDifferentClient = await authHelper.getTestUser( + testUsers.UserRoutingEnabled.userId + ); userSharedClient = await authHelper.getTestUser(testUsers.User7.userId); + userRoutingDisabled = await authHelper.getTestUser(testUsers.User2.userId); routingConfigNoUpdates = RoutingConfigFactory.create(user1); routingConfigSuccessfullyUpdate = RoutingConfigFactory.create(user1); @@ -259,4 +263,25 @@ test.describe('PUT /v1/routing-configuration/:routingConfigId', () => { }, }); }); + + test('returns 400 if routing feature is disabled on the client', async ({ + request, + }) => { + const response = await request.put( + `${process.env.API_BASE_URL}/v1/routing-configuration/some-routing-config`, + { + headers: { + Authorization: await userRoutingDisabled.getAccessToken(), + }, + data: RoutingConfigFactory.create(userRoutingDisabled).apiPayload, + } + ); + + expect(response.status()).toBe(400); + + expect(await response.json()).toEqual({ + statusCode: 400, + technicalMessage: 'Routing feature is disabled', + }); + }); });