diff --git a/Dockerfile b/Dockerfile index 33e0b8b..bfc1705 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN npm install aws-aws-distro-opentelemetry-node-autoinstrumentation-$(node -p RUN npm install # Stage 2: Build the cp-utility binary -FROM public.ecr.aws/docker/library/rust:1.75 as builder +FROM public.ecr.aws/docker/library/rust:1.81 as builder WORKDIR /usr/src/cp-utility COPY ./tools/cp-utility . diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore b/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore index 34c98dc..8cbd431 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore +++ b/aws-distro-opentelemetry-node-autoinstrumentation/.eslintignore @@ -1,4 +1,5 @@ build node_modules .eslintrc.js -version.ts \ No newline at end of file +version.ts +src/third-party \ No newline at end of file diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/package.json b/aws-distro-opentelemetry-node-autoinstrumentation/package.json index 38b1d2a..78412fa 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/package.json +++ b/aws-distro-opentelemetry-node-autoinstrumentation/package.json @@ -31,9 +31,18 @@ "prepublishOnly": "npm run compile", "tdd": "yarn test -- --watch-extensions ts --watch", "test": "nyc ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", - "test:coverage": "nyc --all --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", + "test:coverage": "nyc --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'", "watch": "tsc -w" }, + "nyc": { + "all": true, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/third-party/**/*.ts" + ] + }, "bugs": { "url": "https://github.com/aws-observability/aws-otel-js-instrumentation/issues" }, @@ -64,7 +73,11 @@ "@aws-sdk/client-bedrock-agent-runtime": "3.632.0", "@aws-sdk/client-bedrock-runtime": "3.632.0", "@aws-sdk/client-kinesis": "3.632.0", + "@aws-sdk/client-lambda": "^3.632.0", "@aws-sdk/client-s3": "3.632.0", + "@aws-sdk/client-secrets-manager": "^3.632.0", + "@aws-sdk/client-sfn": "^3.632.0", + "@aws-sdk/client-sns": "^3.632.0", "@opentelemetry/contrib-test-utils": "0.41.0", "@types/mocha": "7.0.2", "@types/node": "18.6.5", diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts index e237827..46150b7 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts @@ -14,6 +14,7 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = { AWS_REMOTE_RESOURCE_IDENTIFIER: 'aws.remote.resource.identifier', AWS_SDK_DESCENDANT: 'aws.sdk.descendant', AWS_CONSUMER_PARENT_SPAN_KIND: 'aws.consumer.parent.span.kind', + AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: 'aws.remote.resource.cfn.primary.identifier', AWS_REMOTE_TARGET: 'aws.remote.target', AWS_REMOTE_DB_USER: 'aws.remote.db.user', @@ -35,4 +36,12 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = { AWS_BEDROCK_KNOWLEDGE_BASE_ID: 'aws.bedrock.knowledge_base.id', AWS_BEDROCK_AGENT_ID: 'aws.bedrock.agent.id', AWS_BEDROCK_GUARDRAIL_ID: 'aws.bedrock.guardrail.id', + AWS_BEDROCK_GUARDRAIL_ARN: 'aws.bedrock.guardrail.arn', + AWS_SNS_TOPIC_ARN: 'aws.sns.topic.arn', + AWS_SECRETSMANAGER_SECRET_ARN: 'aws.secretsmanager.secret.arn', + AWS_STEPFUNCTIONS_STATEMACHINE_ARN: 'aws.stepfunctions.state_machine.arn', + AWS_STEPFUNCTIONS_ACTIVITY_ARN: 'aws.stepfunctions.activity.arn', + AWS_LAMBDA_FUNCTION_NAME: 'aws.lambda.function.name', + AWS_LAMBDA_RESOURCE_MAPPING_ID: 'aws.lambda.resource_mapping.id', + AWS_LAMBDA_FUNCTION_ARN: 'aws.lambda.function.arn', }; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts index e58b600..ae318a3 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts @@ -55,6 +55,10 @@ const NORMALIZED_DYNAMO_DB_SERVICE_NAME: string = 'AWS::DynamoDB'; const NORMALIZED_KINESIS_SERVICE_NAME: string = 'AWS::Kinesis'; const NORMALIZED_S3_SERVICE_NAME: string = 'AWS::S3'; const NORMALIZED_SQS_SERVICE_NAME: string = 'AWS::SQS'; +const NORMALIZED_SNS_SERVICE_NAME: string = 'AWS::SNS'; +const NORMALIZED_SECRETSMANAGER_SERVICE_NAME = 'AWS::SecretsManager'; +const NORMALIZED_STEPFUNCTIONS_SERVICE_NAME = 'AWS::StepFunctions'; +const NORMALIZED_LAMBDA_SERVICE_NAME = 'AWS::Lambda'; const NORMALIZED_BEDROCK_SERVICE_NAME: string = 'AWS::Bedrock'; const NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME: string = 'AWS::BedrockRuntime'; @@ -330,6 +334,8 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { BedrockAgent: NORMALIZED_BEDROCK_SERVICE_NAME, BedrockAgentRuntime: NORMALIZED_BEDROCK_SERVICE_NAME, BedrockRuntime: NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME, + SecretsManager: NORMALIZED_SECRETSMANAGER_SERVICE_NAME, + SFN: NORMALIZED_STEPFUNCTIONS_SERVICE_NAME, }; return awsSdkServiceMapping[serviceName] || 'AWS::' + serviceName; } @@ -350,6 +356,7 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { private static setRemoteResourceTypeAndIdentifier(span: ReadableSpan, attributes: Attributes): void { let remoteResourceType: AttributeValue | undefined; let remoteResourceIdentifier: AttributeValue | undefined; + let cloudFormationIdentifier: AttributeValue | undefined; if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { const awsTableNames: AttributeValue | undefined = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES]; @@ -370,16 +377,56 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_S3_BUCKET] ); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN)) { + const snsArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN]; + + remoteResourceType = NORMALIZED_SNS_SERVICE_NAME + '::Topic'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(snsArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(snsArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN)) { + const secretsArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]; + + remoteResourceType = NORMALIZED_SECRETSMANAGER_SERVICE_NAME + '::Secret'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(secretsArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(secretsArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN)) { + const stateMachineArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN]; + + remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::StateMachine'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(stateMachineArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(stateMachineArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN)) { + const activityArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN]; + + remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::Activity'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + this.extractResourceNameFromArn(activityArn) + ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(activityArn); + } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID)) { + remoteResourceType = NORMALIZED_LAMBDA_SERVICE_NAME + '::EventSourceMapping'; + remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID] + ); } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME)) { remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME] ); } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL)) { - remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue'; - remoteResourceIdentifier = SqsUrlParser.getQueueName( - AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL]) + const sqsQueueUrl = AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL] ); + + remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue'; + remoteResourceIdentifier = SqsUrlParser.getQueueName(sqsQueueUrl); + cloudFormationIdentifier = sqsQueueUrl; } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID)) { remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Agent'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( @@ -390,11 +437,17 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID] ); + cloudFormationIdentifier = `${AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID] + )}|${remoteResourceIdentifier}`; } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID)) { remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Guardrail'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID] ); + cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( + span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN] + ); } else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID)) { remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::KnowledgeBase'; remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters( @@ -414,6 +467,14 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { if (remoteResourceType !== undefined && remoteResourceIdentifier !== undefined) { attributes[AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_TYPE] = remoteResourceType; attributes[AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_IDENTIFIER] = remoteResourceIdentifier; + + if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) { + if (cloudFormationIdentifier === undefined) { + cloudFormationIdentifier = remoteResourceIdentifier; + } + + attributes[AWS_ATTRIBUTE_KEYS.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER] = cloudFormationIdentifier; + } } } @@ -532,6 +593,16 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator { return input.split('^').join('^^').split('|').join('^|'); } + // Extracts the name of the resource from an arn + private static extractResourceNameFromArn(attribute: AttributeValue | undefined): string | undefined { + if (typeof attribute === 'string' && attribute.startsWith('arn:aws:')) { + const split = attribute.split(':'); + return split[split.length - 1]; + } + + return undefined; + } + /** Span kind is needed for differentiating metrics in the EMF exporter */ private static setSpanKindForService(span: ReadableSpan, attributes: Attributes): void { let spanKind: string = SpanKind[span.kind]; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts index 0072900..68fa3fe 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/bedrock.ts @@ -15,8 +15,9 @@ const AGENT_ID: string = 'agentId'; const KNOWLEDGE_BASE_ID: string = 'knowledgeBaseId'; const DATA_SOURCE_ID: string = 'dataSourceId'; const GUARDRAIL_ID: string = 'guardrailId'; +const GUARDRAIL_ARN: string = 'guardrailArn'; const MODEL_ID: string = 'modelId'; -const AWS_BEDROCK_SYSTEM: string = 'aws_bedrock'; +const AWS_BEDROCK_SYSTEM: string = 'aws.bedrock'; const AGENT_OPERATIONS = [ 'CreateAgentActionGroup', @@ -60,6 +61,7 @@ const knowledgeBaseOperationAttributeKeyMapping = { }; const dataSourceOperationAttributeKeyMapping = { [AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]: DATA_SOURCE_ID, + [AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]: KNOWLEDGE_BASE_ID, }; // This map allows us to get all relevant attribute key mappings for a given operation @@ -182,10 +184,15 @@ export class BedrockServiceExtension implements ServiceExtension { }; } responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void { - const guardrail_id = response.data[GUARDRAIL_ID]; + const guardrailId = response.data[GUARDRAIL_ID]; + const guardrailArn = response.data[GUARDRAIL_ARN]; + + if (guardrailId) { + span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID, guardrailId); + } - if (guardrail_id) { - span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID, guardrail_id); + if (guardrailArn) { + span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN, guardrailArn); } } } @@ -225,6 +232,16 @@ export class BedrockRuntimeServiceExtension implements ServiceExtension { spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.textGenerationConfig.maxTokenCount; } + } else if (modelId.includes('amazon.nova')) { + if (requestBody.inferenceConfig?.temperature !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE] = requestBody.inferenceConfig.temperature; + } + if (requestBody.inferenceConfig?.top_p !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P] = requestBody.inferenceConfig.top_p; + } + if (requestBody.inferenceConfig?.max_new_tokens !== undefined) { + spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.inferenceConfig.max_new_tokens; + } } else if (modelId.includes('anthropic.claude')) { if (requestBody.max_tokens !== undefined) { spanAttributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS] = requestBody.max_tokens; @@ -328,6 +345,18 @@ export class BedrockRuntimeServiceExtension implements ServiceExtension { responseBody.results[0].completionReason, ]); } + } else if (currentModelId.includes('amazon.nova')) { + if (responseBody.usage !== undefined) { + if (responseBody.usage.inputTokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.usage.inputTokens); + } + if (responseBody.usage.outputTokens !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS, responseBody.usage.outputTokens); + } + } + if (responseBody.stopReason !== undefined) { + span.setAttribute(AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS, [responseBody.stopReason]); + } } else if (currentModelId.includes('anthropic.claude')) { if (responseBody.usage?.input_tokens !== undefined) { span.setAttribute(AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS, responseBody.usage.input_tokens); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/secretsmanager.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/secretsmanager.ts new file mode 100644 index 0000000..bf78434 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/secretsmanager.ts @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Attributes, Span, SpanKind, Tracer } from '@opentelemetry/api'; +import { + AwsSdkInstrumentationConfig, + NormalizedRequest, + NormalizedResponse, +} from '@opentelemetry/instrumentation-aws-sdk'; +import { AWS_ATTRIBUTE_KEYS } from '../../../aws-attribute-keys'; +import { RequestMetadata, ServiceExtension } from '../../../third-party/otel/aws/services/ServiceExtension'; + +export class SecretsManagerServiceExtension implements ServiceExtension { + requestPreSpanHook(request: NormalizedRequest, _config: AwsSdkInstrumentationConfig): RequestMetadata { + const secretId = request.commandInput?.SecretId; + + const spanKind: SpanKind = SpanKind.CLIENT; + let spanName: string | undefined; + + const spanAttributes: Attributes = {}; + + if (typeof secretId === 'string' && secretId.startsWith('arn:aws:secretsmanager:')) { + spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN] = secretId; + } + + const isIncoming = false; + + return { + isIncoming, + spanAttributes, + spanKind, + spanName, + }; + } + + responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void { + const secretArn = response.data.ARN; + + if (secretArn) { + span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, secretArn); + } + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/step-functions.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/step-functions.ts new file mode 100644 index 0000000..4271cb0 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/step-functions.ts @@ -0,0 +1,36 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Attributes, SpanKind } from '@opentelemetry/api'; +import { AwsSdkInstrumentationConfig, NormalizedRequest } from '@opentelemetry/instrumentation-aws-sdk'; +import { AWS_ATTRIBUTE_KEYS } from '../../../aws-attribute-keys'; +import { RequestMetadata, ServiceExtension } from '../../../third-party/otel/aws/services/ServiceExtension'; + +export class StepFunctionsServiceExtension implements ServiceExtension { + requestPreSpanHook(request: NormalizedRequest, _config: AwsSdkInstrumentationConfig): RequestMetadata { + const stateMachineArn = request.commandInput?.stateMachineArn; + const activityArn = request.commandInput?.activityArn; + + const spanKind: SpanKind = SpanKind.CLIENT; + let spanName: string | undefined; + + const spanAttributes: Attributes = {}; + + if (stateMachineArn) { + spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN] = stateMachineArn; + } + + if (activityArn) { + spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN] = activityArn; + } + + const isIncoming = false; + + return { + isIncoming, + spanAttributes, + spanKind, + spanName, + }; + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/aws-lambda.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-lambda.ts similarity index 100% rename from aws-distro-opentelemetry-node-autoinstrumentation/src/patches/aws/services/aws-lambda.ts rename to aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-lambda.ts diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts new file mode 100644 index 0000000..b3044d6 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; +import { context as otelContext, defaultTextMapSetter } from '@opentelemetry/api'; +import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray'; +import type { Command as AwsV3Command } from '@aws-sdk/types'; + +const awsXrayPropagator = new AWSXRayPropagator(); +const V3_CLIENT_CONFIG_KEY = Symbol('opentelemetry.instrumentation.aws-sdk.client.config'); +type V3PluginCommand = AwsV3Command & { + [V3_CLIENT_CONFIG_KEY]?: any; +}; + +// This class extends the upstream AwsInstrumentation to override its patching mechanism of the `send` method. +// The overriden method will additionally update the AWS SDK middleware stack to inject the `X-Amzn-Trace-Id` HTTP header. +// +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +export class AwsSdkInstrumentationExtended extends AwsInstrumentation { + // Override the upstream private _getV3SmithyClientSendPatch method to add middleware to inject X-Ray Trace Context into HTTP Headers + // https://github.com/open-telemetry/opentelemetry-js-contrib/blob/instrumentation-aws-sdk-v0.48.0/plugins/node/opentelemetry-instrumentation-aws-sdk/src/aws-sdk.ts#L373-L384 + override _getV3SmithyClientSendPatch(original: (...args: unknown[]) => Promise) { + return function send(this: any, command: V3PluginCommand, ...args: unknown[]): Promise { + this.middlewareStack?.add( + (next: any, context: any) => async (middlewareArgs: any) => { + awsXrayPropagator.inject(otelContext.active(), middlewareArgs.request.headers, defaultTextMapSetter); + const result = await next(middlewareArgs); + return result; + }, + { + step: 'build', + name: '_adotInjectXrayContextMiddleware', + override: true, + } + ); + + command[V3_CLIENT_CONFIG_KEY] = this.config; + return original.apply(this, [command, ...args]); + }; + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts index 7cc98bd..74879b9 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts @@ -25,7 +25,11 @@ import { } from './aws/services/bedrock'; import { KinesisServiceExtension } from './aws/services/kinesis'; import { S3ServiceExtension } from './aws/services/s3'; -import { AwsLambdaInstrumentationPatch } from './aws/services/aws-lambda'; +import { SecretsManagerServiceExtension } from './aws/services/secretsmanager'; +import { StepFunctionsServiceExtension } from './aws/services/step-functions'; +import { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node'; +import { AwsSdkInstrumentationExtended } from './extended-instrumentations/aws-sdk-instrumentation-extended'; +import { AwsLambdaInstrumentationPatch } from './extended-instrumentations/aws-lambda'; export const traceContextEnvironmentKey = '_X_AMZN_TRACE_ID'; const awsPropagator = new AWSXRayPropagator(); @@ -38,7 +42,10 @@ export const headerGetter: TextMapGetter = { }, }; -export function applyInstrumentationPatches(instrumentations: Instrumentation[]): void { +export function applyInstrumentationPatches( + instrumentations: Instrumentation[], + instrumentationConfigs?: InstrumentationConfigMap +): void { /* Apply patches to upstream instrumentation libraries. @@ -50,18 +57,28 @@ export function applyInstrumentationPatches(instrumentations: Instrumentation[]) */ instrumentations.forEach((instrumentation, index) => { if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-sdk') { + diag.debug('Overriding aws sdk instrumentation'); + instrumentations[index] = new AwsSdkInstrumentationExtended( + instrumentationConfigs ? instrumentationConfigs['@opentelemetry/instrumentation-aws-sdk'] : undefined + ); + // Access private property servicesExtensions of AwsInstrumentation // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const services: Map | undefined = (instrumentation as any).servicesExtensions?.services; + const services: Map | undefined = (instrumentations[index] as any).servicesExtensions + ?.services; if (services) { services.set('S3', new S3ServiceExtension()); services.set('Kinesis', new KinesisServiceExtension()); + services.set('SecretsManager', new SecretsManagerServiceExtension()); + services.set('SFN', new StepFunctionsServiceExtension()); services.set('Bedrock', new BedrockServiceExtension()); services.set('BedrockAgent', new BedrockAgentServiceExtension()); services.set('BedrockAgentRuntime', new BedrockAgentRuntimeServiceExtension()); services.set('BedrockRuntime', new BedrockRuntimeServiceExtension()); patchSqsServiceExtension(services.get('SQS')); + patchSnsServiceExtension(services.get('SNS')); + patchLambdaServiceExtension(services.get('Lambda')); } } else if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-lambda') { diag.debug('Overriding aws lambda instrumentation'); @@ -143,3 +160,65 @@ function patchSqsServiceExtension(sqsServiceExtension: any): void { sqsServiceExtension.requestPreSpanHook = patchedRequestPreSpanHook; } } + +/* + * This patch extends the existing upstream extension for SNS. Extensions allow for custom logic for adding + * service-specific information to spans, such as attributes. Specifically, we are adding logic to add + * `aws.sns.topic.arn` attribute, to be used to generate RemoteTarget and achieve parity with the Java/Python instrumentation. + * + * + * @param snsServiceExtension SNS Service Extension obtained the service extension list from the AWS SDK OTel Instrumentation + */ +function patchSnsServiceExtension(snsServiceExtension: any): void { + if (snsServiceExtension) { + const requestPreSpanHook = snsServiceExtension.requestPreSpanHook; + snsServiceExtension._requestPreSpanHook = requestPreSpanHook; + + const patchedRequestPreSpanHook = ( + request: NormalizedRequest, + _config: AwsSdkInstrumentationConfig + ): RequestMetadata => { + const requestMetadata: RequestMetadata = snsServiceExtension._requestPreSpanHook(request, _config); + if (requestMetadata.spanAttributes) { + const topicArn = request.commandInput?.TopicArn; + if (topicArn) { + requestMetadata.spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN] = topicArn; + } + } + return requestMetadata; + }; + + snsServiceExtension.requestPreSpanHook = patchedRequestPreSpanHook; + } +} + +/* + * This patch extends the existing upstream extension for Lambda. Extensions allow for custom logic for adding + * service-specific information to spans, such as attributes. Specifically, we are adding logic to add + * `aws.lambda.resource_mapping.id` attribute, to be used to generate RemoteTarget and achieve parity with the Java/Python instrumentation. + * + * + * @param lambdaServiceExtension Lambda Service Extension obtained the service extension list from the AWS SDK OTel Instrumentation + */ +function patchLambdaServiceExtension(lambdaServiceExtension: any): void { + if (lambdaServiceExtension) { + const requestPreSpanHook = lambdaServiceExtension.requestPreSpanHook; + lambdaServiceExtension._requestPreSpanHook = requestPreSpanHook; + + const patchedRequestPreSpanHook = ( + request: NormalizedRequest, + _config: AwsSdkInstrumentationConfig + ): RequestMetadata => { + const requestMetadata: RequestMetadata = lambdaServiceExtension._requestPreSpanHook(request, _config); + if (requestMetadata.spanAttributes) { + const resourceMappingId = request.commandInput?.UUID; + if (resourceMappingId) { + requestMetadata.spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID] = resourceMappingId; + } + } + return requestMetadata; + }; + + lambdaServiceExtension.requestPreSpanHook = patchedRequestPreSpanHook; + } +} diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts index 823f164..751c0ff 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/register.ts @@ -62,7 +62,7 @@ const instrumentationConfigs: InstrumentationConfigMap = { const instrumentations: Instrumentation[] = getNodeAutoInstrumentations(instrumentationConfigs); // Apply instrumentation patches -applyInstrumentationPatches(instrumentations); +applyInstrumentationPatches(instrumentations, instrumentationConfigs); const configurator: AwsOpentelemetryConfigurator = new AwsOpentelemetryConfigurator(instrumentations, useXraySampler); const configuration: Partial = configurator.configure(); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts index e74eda3..0e0a7f2 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-metric-attribute-generator.test.ts @@ -761,6 +761,39 @@ describe('AwsMetricAttributeGeneratorTest', () => { validateRemoteResourceAttributes('AWS::Kinesis::Stream', 'AWS_KINESIS_STREAM_NAME'); mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_NAME, undefined); + // Validate behaviour of AWS_SNS_TOPIC_ARN attribute then remove it. + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN, 'arn:aws:sns:us-east-1:123456789012:testTopic'); + validateRemoteResourceAttributes('AWS::SNS::Topic', 'testTopic'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN, undefined); + + // Validate behaviour of AWS_SECRETSMANAGER_SECRET_ARN attributes then remove it. + mockAttribute( + AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, + 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testSecret' + ); + validateRemoteResourceAttributes('AWS::SecretsManager::Secret', 'testSecret'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, undefined); + + // Validate behaviour of AWS_LAMBDA_RESOURCE_MAPPING_ID attribute then remove it. + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID, 'aws_lambda_resource_mapping_id'); + validateRemoteResourceAttributes('AWS::Lambda::EventSourceMapping', 'aws_lambda_resource_mapping_id'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID, undefined); + + // Validate behaviour of AWS_STEPFUNCTIONS_STATEMACHINE_ARN and AWS_STEPFUNCTIONS_ACTIVITY_ARN attributes then remove them. + mockAttribute( + AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN, + 'arn:aws:states:us-east-1:123456789123:stateMachine:testStateMachine' + ); + validateRemoteResourceAttributes('AWS::StepFunctions::StateMachine', 'testStateMachine'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN, undefined); + + mockAttribute( + AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN, + 'arn:aws:states:us-east-1:123456789123:activity:testActivity' + ); + validateRemoteResourceAttributes('AWS::StepFunctions::Activity', 'testActivity'); + mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN, undefined); + // Validate behaviour of AWS_TABLE_NAMES attribute with one table name, then remove it. mockAttribute(AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES, ['aws_table_name']); validateRemoteResourceAttributes('AWS::DynamoDB::Table', 'aws_table_name'); @@ -1167,6 +1200,10 @@ describe('AwsMetricAttributeGeneratorTest', () => { testAwsSdkServiceNormalization('Kinesis', 'AWS::Kinesis'); testAwsSdkServiceNormalization('S3', 'AWS::S3'); testAwsSdkServiceNormalization('SQS', 'AWS::SQS'); + testAwsSdkServiceNormalization('SNS', 'AWS::SNS'); + testAwsSdkServiceNormalization('Lambda', 'AWS::Lambda'); + testAwsSdkServiceNormalization('SecretsManager', 'AWS::SecretsManager'); + testAwsSdkServiceNormalization('SFN', 'AWS::StepFunctions'); testAwsSdkServiceNormalization('Bedrock', 'AWS::Bedrock'); testAwsSdkServiceNormalization('BedrockAgent', 'AWS::Bedrock'); testAwsSdkServiceNormalization('BedrockAgentRuntime', 'AWS::Bedrock'); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts index 8cc5a2b..1759774 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/bedrock.test.ts @@ -134,7 +134,7 @@ describe('BedrockAgent', () => { expect(getDataSourceSpans.length).toBe(1); const getDataSourceSpan = getDataSourceSpans[0]; expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); - expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBe(dummyKnowledgeBaseId); expect(getDataSourceSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBe(dummyDataSourceId); expect(getDataSourceSpan.kind).toBe(SpanKind.CLIENT); }); @@ -236,9 +236,11 @@ describe('Bedrock', () => { describe('GetGuardrail', () => { it('adds guardrailId to span', async () => { const dummyGuardrailIdentifier: string = 'ABCDEFGH'; + const guardrailArn: string = 'arn:aws:bedrock:us-east-1:123456789012:guardrail/abc123'; nock(`https://bedrock.${region}.amazonaws.com`).get(`/guardrails/${dummyGuardrailIdentifier}`).reply(200, { guardrailId: dummyGuardrailIdentifier, + guardrailArn: guardrailArn, }); await bedrock @@ -258,6 +260,7 @@ describe('Bedrock', () => { expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]).toBe(dummyGuardrailIdentifier); + expect(getGuardrailSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN]).toBe(guardrailArn); expect(getGuardrailSpan.kind).toBe(SpanKind.CLIENT); }); }); @@ -331,7 +334,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.6); @@ -384,7 +387,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(4096); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0); @@ -397,6 +400,60 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); }); + it('Add Amazon Nova model attributes to span', async () => { + const modelId: string = 'amazon.nova-pro-v1:0'; + const prompt: string = 'Campfire story'; + const mockRequestBody: string = JSON.stringify({ + inputText: prompt, + inferenceConfig: { + max_new_tokens: 500, + temperature: 0.9, + top_p: 0.7, + }, + }); + const mockResponseBody: any = { + output: { message: { content: [{ text: '' }], role: 'assistant' } }, + stopReason: 'max_tokens', + usage: { inputTokens: 432, outputTokens: 681 }, + + request: { + commandInput: { + modelId: modelId, + }, + }, + }; + + nock(`https://bedrock-runtime.${region}.amazonaws.com`) + .post(`/model/${encodeURIComponent(modelId)}/invoke`) + .reply(200, mockResponseBody); + + await bedrock + .invokeModel({ + modelId: modelId, + body: mockRequestBody, + }) + .catch((err: any) => {}); + + const testSpans: ReadableSpan[] = getTestSpans(); + const invokeModelSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'BedrockRuntime.InvokeModel'; + }); + expect(invokeModelSpans.length).toBe(1); + const invokeModelSpan = invokeModelSpans[0]; + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(500); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.9); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TOP_P]).toBe(0.7); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_INPUT_TOKENS]).toBe(432); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_USAGE_OUTPUT_TOKENS]).toBe(681); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_RESPONSE_FINISH_REASONS]).toEqual(['max_tokens']); + expect(invokeModelSpan.kind).toBe(SpanKind.CLIENT); + }); + it('Add Anthropic Claude model attributes to span', async () => { const modelId: string = 'anthropic.claude-3-5-sonnet-20240620-v1:0'; const prompt: string = 'Complete this text. It was the best of times it was the worst...'; @@ -448,7 +505,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(1000); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(1.0); @@ -506,7 +563,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.5); @@ -560,7 +617,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.5); @@ -614,7 +671,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(512); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.5); @@ -676,7 +733,7 @@ describe('BedrockRuntime', () => { expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]).toBeUndefined(); expect(invokeModelSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]).toBeUndefined(); - expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws_bedrock'); + expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_SYSTEM]).toBe('aws.bedrock'); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]).toBe(modelId); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MAX_TOKENS]).toBe(4096); expect(invokeModelSpan.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_TEMPERATURE]).toBe(0.75); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/secretsmanager.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/secretsmanager.test.ts new file mode 100644 index 0000000..7e84777 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/secretsmanager.test.ts @@ -0,0 +1,100 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils'; +import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; +import { applyInstrumentationPatches } from './../../../../src/patches/instrumentation-patch'; + +const instrumentations: AwsInstrumentation[] = [new AwsInstrumentation()]; +applyInstrumentationPatches(instrumentations); +registerInstrumentationTesting(instrumentations[0]); + +import { SecretsManager } from '@aws-sdk/client-secrets-manager'; +import * as nock from 'nock'; + +import { SpanKind } from '@opentelemetry/api'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { expect } from 'expect'; +import { AWS_ATTRIBUTE_KEYS } from '../../../../src/aws-attribute-keys'; + +const region = 'us-east-1'; + +describe('SecretsManager', () => { + let secretsManager: SecretsManager; + beforeEach(() => { + secretsManager = new SecretsManager({ + region: region, + credentials: { + accessKeyId: 'abcde', + secretAccessKey: 'abcde', + }, + }); + }); + + describe('DescribeSecret', () => { + const testParams = [ + 'testId', + 'badarn:aws:secretsmanager:us-weast-1:123456789123:secret:testId123456', + 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456', + ]; + + testParams.forEach(secretId => { + it('should generate secret arn attribute only if secretId is an valid ARN', async () => { + nock(`https://secretsmanager.${region}.amazonaws.com/`).post('/').reply(200, 'null'); + + await secretsManager + .describeSecret({ + SecretId: secretId, + }) + .catch((err: any) => {}); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getDescribeSecretSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SecretsManager.DescribeSecret'; + }); + + expect(getDescribeSecretSpans.length).toBe(1); + const describeSecretSpan = getDescribeSecretSpans[0]; + + if (secretId.startsWith('arn:aws:secretsmanager:')) { + expect(describeSecretSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(secretId); + } else { + expect(describeSecretSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBeUndefined(); + } + + expect(describeSecretSpan.kind).toBe(SpanKind.CLIENT); + }); + }); + }); + + describe('GetSecretValue', () => { + it('secret arn attribute should be populated from the response', async () => { + const secretIdArn = 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456'; + + nock(`https://secretsmanager.${region}.amazonaws.com/`).post('/').reply(200, { + ARN: secretIdArn, + Name: 'testId', + }); + + await secretsManager + .getSecretValue({ + SecretId: 'testSecret', + }) + .catch((err: any) => { + console.log(err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getSecretValueSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SecretsManager.GetSecretValue'; + }); + + expect(getSecretValueSpans.length).toBe(1); + + const secretValueSpan = getSecretValueSpans[0]; + + expect(secretValueSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(secretIdArn); + expect(secretValueSpan.kind).toBe(SpanKind.CLIENT); + }); + }); +}); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/step-functions.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/step-functions.test.ts new file mode 100644 index 0000000..8d9a5a5 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/step-functions.test.ts @@ -0,0 +1,93 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils'; +import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; +import { applyInstrumentationPatches } from './../../../../src/patches/instrumentation-patch'; + +const instrumentations: AwsInstrumentation[] = [new AwsInstrumentation()]; +applyInstrumentationPatches(instrumentations); +registerInstrumentationTesting(instrumentations[0]); + +import { SFN } from '@aws-sdk/client-sfn'; +import * as nock from 'nock'; + +import { SpanKind } from '@opentelemetry/api'; +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { expect } from 'expect'; +import { AWS_ATTRIBUTE_KEYS } from '../../../../src/aws-attribute-keys'; + +const region = 'us-east-1'; + +describe('SFN', () => { + let sfn: SFN; + beforeEach(() => { + sfn = new SFN({ + region: region, + credentials: { + accessKeyId: 'abcde', + secretAccessKey: 'abcde', + }, + }); + }); + + describe('DescribeStateMachine', () => { + it('span has stateMachineArn in its attributes', async () => { + const stateMachineArn: string = 'arn:aws:states:us-east-1:123456789123:stateMachine:testStateMachine'; + + nock(`https://states.${region}.amazonaws.com/`).post('/').reply(200, 'null'); + + await sfn + .describeStateMachine({ + stateMachineArn: stateMachineArn, + }) + .catch((err: any) => { + console.log(err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getStateMachineAttributeSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SFN.DescribeStateMachine'; + }); + + expect(getStateMachineAttributeSpans.length).toBe(1); + + const stateMachineAttributeSpan = getStateMachineAttributeSpans[0]; + + expect(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN in stateMachineAttributeSpan.attributes).toBe(true); + expect(stateMachineAttributeSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN]).toBe( + stateMachineArn + ); + expect(stateMachineAttributeSpan.kind).toBe(SpanKind.CLIENT); + }); + }); + + describe('DescribeActivity', () => { + it('span has activityArn in its attributes', async () => { + const activityArn: string = 'arn:aws:states:us-east-1:123456789123:activity:testActivity'; + + nock(`https://states.${region}.amazonaws.com/`).post('/').reply(200, 'null'); + + await sfn + .describeActivity({ + activityArn: activityArn, + }) + .catch((err: any) => { + console.log(err); + }); + + const testSpans: ReadableSpan[] = getTestSpans(); + const getActivityAttributeSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'SFN.DescribeActivity'; + }); + + expect(getActivityAttributeSpans.length).toBe(1); + + const activityAttributeSpan = getActivityAttributeSpans[0]; + + expect(AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN in activityAttributeSpan.attributes).toBe(true); + expect(activityAttributeSpan.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN]).toBe(activityArn); + expect(activityAttributeSpan.kind).toBe(SpanKind.CLIENT); + }); + }); +}); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts similarity index 98% rename from aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts rename to aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts index d68e1e6..0580769 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { diag } from '@opentelemetry/api'; import { InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; -import { AwsLambdaInstrumentationPatch } from '../../../../src/patches/aws/services/aws-lambda'; +import { AwsLambdaInstrumentationPatch } from '../../../src/patches/extended-instrumentations/aws-lambda'; describe('AwsLambdaInstrumentationPatch', () => { let instrumentation: AwsLambdaInstrumentationPatch; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-sdk-instrumentation-extended.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-sdk-instrumentation-extended.test.ts new file mode 100644 index 0000000..9751d21 --- /dev/null +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-sdk-instrumentation-extended.test.ts @@ -0,0 +1,48 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import * as sinon from 'sinon'; +import { AwsSdkInstrumentationExtended } from '../../../src/patches/extended-instrumentations/aws-sdk-instrumentation-extended'; +import expect from 'expect'; +import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray'; +import { Context, TextMapSetter } from '@opentelemetry/api'; + +describe('AwsSdkInstrumentationExtended', () => { + let instrumentation: AwsSdkInstrumentationExtended; + + beforeEach(() => { + instrumentation = new AwsSdkInstrumentationExtended({}); + }); + + afterEach(() => { + sinon.restore(); + }); + + it('overridden _getV3SmithyClientSendPatch updates MiddlewareStack', async () => { + const mockedMiddlewareStackInternal: any = []; + const mockedMiddlewareStack = { + add: (arg1: any, arg2: any) => mockedMiddlewareStackInternal.push([arg1, arg2]), + }; + const send = instrumentation + ._getV3SmithyClientSendPatch((...args: unknown[]) => Promise.resolve()) + .bind({ middlewareStack: mockedMiddlewareStack }); + sinon + .stub(AWSXRayPropagator.prototype, 'inject') + .callsFake((context: Context, carrier: unknown, setter: TextMapSetter) => { + (carrier as any)['isCarrierModified'] = 'carrierIsModified'; + }); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + await send({}, null); + + const middlewareArgs: any = { + request: { + headers: {}, + }, + }; + await mockedMiddlewareStackInternal[0][0]((arg: any) => Promise.resolve(), null)(middlewareArgs); + + expect(middlewareArgs.request.headers['isCarrierModified']).toEqual('carrierIsModified'); + expect(mockedMiddlewareStackInternal[0][1].name).toEqual('_adotInjectXrayContextMiddleware'); + }); +}); diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts index 385c7d7..6a731f2 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts @@ -23,16 +23,27 @@ import * as sinon from 'sinon'; import { AWSXRAY_TRACE_ID_HEADER, AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray'; import { Context } from 'aws-lambda'; import { SinonStub } from 'sinon'; +import { S3 } from '@aws-sdk/client-s3'; +import nock = require('nock'); +import { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils'; +import { AwsSdkInstrumentationExtended } from '../../src/patches/extended-instrumentations/aws-sdk-instrumentation-extended'; const _STREAM_NAME: string = 'streamName'; const _BUCKET_NAME: string = 'bucketName'; const _QUEUE_NAME: string = 'queueName'; +const _ACTIVITY_ARN: string = 'arn:aws:states:us-east-1:123456789123:activity:testActivity'; +const _STATE_MACHINE_ARN: string = 'arn:aws:states:us-east-1:123456789123:stateMachine:testStateMachine'; +const _SECRETS_ARN: string = 'arn:aws:secretsmanager:us-east-1:123456789123:secret:testId123456'; +const _UUID: string = 'random-uuid'; +const _TOPIC_ARN: string = 'arn:aws:sns:us-east-1:123456789012:mystack-mytopic-NZJ5JSMVGFIE'; const _QUEUE_URL: string = 'https://sqs.us-east-1.amazonaws.com/123412341234/queueName'; const _BEDROCK_AGENT_ID: string = 'agentId'; const _BEDROCK_DATASOURCE_ID: string = 'DataSourceId'; const _BEDROCK_GUARDRAIL_ID: string = 'GuardrailId'; +const _BEDROCK_GUARDRAIL_ARN: string = 'arn:aws:bedrock:us-east-1:123456789012:guardrail/abc123'; const _BEDROCK_KNOWLEDGEBASE_ID: string = 'KnowledgeBaseId'; -const _GEN_AI_SYSTEM: string = 'aws_bedrock'; +const _GEN_AI_SYSTEM: string = 'aws.bedrock'; const _GEN_AI_REQUEST_MODEL: string = 'genAiReuqestModelId'; const mockHeaders = { @@ -45,6 +56,10 @@ const UNPATCHED_INSTRUMENTATIONS: Instrumentation[] = getNodeAutoInstrumentation const PATCHED_INSTRUMENTATIONS: Instrumentation[] = getNodeAutoInstrumentations(); applyInstrumentationPatches(PATCHED_INSTRUMENTATIONS); +const extendedAwsSdkInstrumentation: AwsInstrumentation = new AwsInstrumentation(); +applyInstrumentationPatches([extendedAwsSdkInstrumentation]); +registerInstrumentationTesting(extendedAwsSdkInstrumentation); + describe('InstrumentationPatchTest', () => { it('SanityTestUnpatchedAwsSdkInstrumentation', () => { const awsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); @@ -53,11 +68,18 @@ describe('InstrumentationPatchTest', () => { // Not from patching expect(services.has('SQS')).toBeTruthy(); expect(services.has('SNS')).toBeTruthy(); - expect(services.has('DynamoDB')).toBeTruthy(); expect(services.has('Lambda')).toBeTruthy(); + + expect(services.has('DynamoDB')).toBeTruthy(); // From patching but shouldn't be applied + expect(services.get('SecretsManager')).toBeFalsy(); + expect(services.get('SFN')).toBeFalsy(); expect(services.has('S3')).toBeFalsy(); expect(services.has('Kinesis')).toBeFalsy(); + expect(services.get('SNS')._requestPreSpanHook).toBeFalsy(); + expect(services.get('SNS').requestPreSpanHook).toBeTruthy(); + expect(services.get('Lambda')._requestPreSpanHook).toBeFalsy(); + expect(services.get('Lambda').requestPreSpanHook).toBeTruthy(); expect(services.get('SQS')._requestPreSpanHook).toBeFalsy(); expect(services.get('SQS').requestPreSpanHook).toBeTruthy(); expect(services.has('Bedrock')).toBeFalsy(); @@ -78,9 +100,16 @@ describe('InstrumentationPatchTest', () => { expect(services.has('SNS')).toBeTruthy(); expect(services.has('DynamoDB')).toBeTruthy(); expect(services.has('Lambda')).toBeTruthy(); + // From patching + expect(services.has('SecretsManager')).toBeTruthy(); + expect(services.has('SFN')).toBeTruthy(); expect(services.has('S3')).toBeTruthy(); expect(services.has('Kinesis')).toBeTruthy(); + expect(services.get('SNS')._requestPreSpanHook).toBeTruthy(); + expect(services.get('SNS').requestPreSpanHook).toBeTruthy(); + expect(services.get('Lambda')._requestPreSpanHook).toBeTruthy(); + expect(services.get('Lambda').requestPreSpanHook).toBeTruthy(); expect(services.get('SQS')._requestPreSpanHook).toBeTruthy(); expect(services.get('SQS').requestPreSpanHook).toBeTruthy(); expect(services.has('Bedrock')).toBeTruthy(); @@ -89,6 +118,9 @@ describe('InstrumentationPatchTest', () => { expect(services.get('BedrockRuntime')).toBeTruthy(); // Sanity check expect(services.has('InvalidService')).toBeFalsy(); + + // Check that the original AWS SDK Instrumentation is replaced with the extended version + expect(awsSdkInstrumentation).toBeInstanceOf(AwsSdkInstrumentationExtended); }); it('S3 without patching', () => { @@ -117,6 +149,36 @@ describe('InstrumentationPatchTest', () => { expect(sqsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME]).toBeUndefined(); }); + it('SNS without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractSNSAttributes(services)).not.toThrow(); + + const snsAttributes = doExtractSNSAttributes(services); + expect(snsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN]).toBeUndefined(); + }); + + it('Lambda without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractLambdaAttributes(services)).not.toThrow(); + + const lambdaAttributes: Attributes = doExtractLambdaAttributes(services); + expect(lambdaAttributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID]).toBeUndefined(); + }); + + it('SFN without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractSFNAttributes(services)).toThrow(); + }); + + it('SecretsManager without patching', () => { + const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); + expect(() => doExtractSecretsManagerAttributes(services)).toThrow(); + }); + it('Bedrock without patching', () => { const unpatchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS); const services: Map = extractServicesFromAwsSdkInstrumentation(unpatchedAwsSdkInstrumentation); @@ -137,6 +199,14 @@ describe('InstrumentationPatchTest', () => { expect(kinesisAttributes[AWS_ATTRIBUTE_KEYS.AWS_KINESIS_STREAM_NAME]).toEqual(_STREAM_NAME); }); + it('SNS with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + + const snsAttributes = doExtractSNSAttributes(services); + expect(snsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN]).toBe(_TOPIC_ARN); + }); + it('SQS with patching', () => { const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); @@ -153,6 +223,33 @@ describe('InstrumentationPatchTest', () => { expect(sqsAttributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME]).toEqual(_QUEUE_NAME); }); + it('Lambda with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + const requestLambdaAttributes: Attributes = doExtractLambdaAttributes(services); + expect(requestLambdaAttributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID]).toEqual(_UUID); + }); + + it('SFN with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + const requestSFNAttributes: Attributes = doExtractSFNAttributes(services); + expect(requestSFNAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN]).toEqual(_STATE_MACHINE_ARN); + expect(requestSFNAttributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN]).toEqual(_ACTIVITY_ARN); + }); + + it('SecretsManager with patching', () => { + const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); + const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); + const requestSecretsManagerAttributes: Attributes = doExtractSecretsManagerAttributes(services); + + expect(requestSecretsManagerAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(_SECRETS_ARN); + + const responseHookSecretsManagerAttributes = doResponseHookSecretsManager(services); + + expect(responseHookSecretsManagerAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN]).toBe(_SECRETS_ARN); + }); + it('Bedrock with patching', () => { const patchedAwsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(PATCHED_INSTRUMENTATIONS); const services: Map = extractServicesFromAwsSdkInstrumentation(patchedAwsSdkInstrumentation); @@ -160,8 +257,11 @@ describe('InstrumentationPatchTest', () => { // Expect no-op from attribute extraction in Bedrock expect(Object.entries(bedrockAttributes).length).toEqual(0); const bedrockAttributesAfterResponse: Attributes = doResponseHookBedrock(services, 'Bedrock'); - expect(Object.entries(bedrockAttributesAfterResponse).length).toBe(1); + expect(Object.entries(bedrockAttributesAfterResponse).length).toBe(2); expect(bedrockAttributesAfterResponse[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]).toEqual(_BEDROCK_GUARDRAIL_ID); + expect(bedrockAttributesAfterResponse[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN]).toEqual( + _BEDROCK_GUARDRAIL_ARN + ); }); it('Bedrock Agent with patching', () => { @@ -196,18 +296,25 @@ describe('InstrumentationPatchTest', () => { ListDataSources: { 'aws.bedrock.knowledge_base.id': _BEDROCK_KNOWLEDGEBASE_ID }, UpdateAgentKnowledgeBase: { 'aws.bedrock.knowledge_base.id': _BEDROCK_KNOWLEDGEBASE_ID }, DeleteDataSource: { 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID }, - GetDataSource: { 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID }, + GetDataSource: { + 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID, + 'aws.bedrock.knowledge_base.id': _BEDROCK_KNOWLEDGEBASE_ID, + }, UpdateDataSource: { 'aws.bedrock.data_source.id': _BEDROCK_DATASOURCE_ID }, }; for (const [operation, attribute_tuple] of Object.entries(operation_to_expected_attribute)) { const bedrockAttributes: Attributes = doExtractBedrockAttributes(services, 'BedrockAgent', operation); - const [attribute_key, attribute_value] = Object.entries(attribute_tuple)[0]; - expect(Object.entries(bedrockAttributes).length).toBe(1); - expect(bedrockAttributes[attribute_key]).toEqual(attribute_value); + + for (const [attribute_key, attribute_value] of Object.entries(attribute_tuple)) { + expect(bedrockAttributes[attribute_key]).toEqual(attribute_value); + } + const bedrockAgentSuccessAttributes: Attributes = doResponseHookBedrock(services, 'BedrockAgent', operation); - expect(Object.entries(bedrockAgentSuccessAttributes).length).toBe(1); - expect(bedrockAgentSuccessAttributes[attribute_key]).toEqual(attribute_value); + + for (const [attribute_key, attribute_value] of Object.entries(attribute_tuple)) { + expect(bedrockAgentSuccessAttributes[attribute_key]).toEqual(attribute_value); + } } }); @@ -303,6 +410,55 @@ describe('InstrumentationPatchTest', () => { return doExtractAttributes(services, serviceName, params); } + function doExtractSNSAttributes(services: Map): Attributes { + const serviceName: string = 'SNS'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + TopicArn: _TOPIC_ARN, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + + function doExtractLambdaAttributes(services: Map): Attributes { + const serviceName: string = 'Lambda'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + UUID: _UUID, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + + function doExtractSFNAttributes(services: Map): Attributes { + const serviceName: string = 'SFN'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + stateMachineArn: _STATE_MACHINE_ARN, + activityArn: _ACTIVITY_ARN, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + + function doExtractSecretsManagerAttributes(services: Map): Attributes { + const serviceName: string = 'SecretsManager'; + const params: NormalizedRequest = { + serviceName: serviceName, + commandName: 'mockCommandName', + commandInput: { + SecretId: _SECRETS_ARN, + }, + }; + return doExtractAttributes(services, serviceName, params); + } + function doExtractBedrockAttributes( services: Map, serviceName: string, @@ -335,6 +491,22 @@ describe('InstrumentationPatchTest', () => { return requestMetadata.spanAttributes || {}; } + function doResponseHookSecretsManager(services: Map): Attributes { + const results: Partial = { + data: { + ARN: _SECRETS_ARN, + }, + + request: { + commandInput: {}, + commandName: 'dummy_operation', + serviceName: 'SecretsManager', + }, + }; + + return doResponseHook(services, 'SecretsManager', results as NormalizedResponse); + } + function doResponseHookBedrock( services: Map, serviceName: string, @@ -346,6 +518,7 @@ describe('InstrumentationPatchTest', () => { dataSourceId: _BEDROCK_DATASOURCE_ID, knowledgeBaseId: _BEDROCK_KNOWLEDGEBASE_ID, guardrailId: _BEDROCK_GUARDRAIL_ID, + guardrailArn: _BEDROCK_GUARDRAIL_ARN, modelId: _GEN_AI_REQUEST_MODEL, }, request: { @@ -388,6 +561,52 @@ describe('InstrumentationPatchTest', () => { expect(filteredInstrumentations.length).toEqual(1); return filteredInstrumentations[0] as AwsLambdaInstrumentation; } + + describe('AwsSdkInstrumentationPatchTest', () => { + let s3: S3; + const region = 'us-east-1'; + + it('injects trace context header into request via propagator', async () => { + s3 = new S3({ + region: region, + credentials: { + accessKeyId: 'abcde', + secretAccessKey: 'abcde', + }, + }); + + const dummyBucketName: string = 'dummy-bucket-name'; + let reqHeaders: any = {}; + + nock(`https://${dummyBucketName}.s3.${region}.amazonaws.com`) + .get('/') + .reply(200, function (uri: any, requestBody: any) { + reqHeaders = this.req.headers; + return 'null'; + }); + + await s3 + .listObjects({ + Bucket: dummyBucketName, + }) + .catch((err: any) => {}); + + const testSpans: ReadableSpan[] = getTestSpans(); + const listObjectsSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => { + return s.name === 'S3.ListObjects'; + }); + + expect(listObjectsSpans.length).toBe(1); + + const traceId = listObjectsSpans[0].spanContext().traceId; + const spanId = listObjectsSpans[0].spanContext().spanId; + expect(reqHeaders['x-amzn-trace-id'] as string).toEqual( + `Root=1-${traceId.substring(0, 8)}-${listObjectsSpans[0] + .spanContext() + .traceId.substring(8, 32)};Parent=${spanId};Sampled=1` + ); + }); + }); }); describe('customExtractor', () => { diff --git a/contract-tests/images/applications/aws-sdk/package.json b/contract-tests/images/applications/aws-sdk/package.json index cef37d8..8596d51 100644 --- a/contract-tests/images/applications/aws-sdk/package.json +++ b/contract-tests/images/applications/aws-sdk/package.json @@ -15,10 +15,16 @@ "@aws-sdk/client-bedrock-agent-runtime": "^3.676.0", "@aws-sdk/client-bedrock-runtime": "^3.675.0", "@aws-sdk/client-dynamodb": "^3.658.1", + "@aws-sdk/client-iam": "^3.696.0", "@aws-sdk/client-kinesis": "^3.658.1", + "@aws-sdk/client-lambda": "^3.698.0", "@aws-sdk/client-s3": "^3.658.1", + "@aws-sdk/client-secrets-manager": "^3.696.0", + "@aws-sdk/client-sfn": "^3.696.0", + "@aws-sdk/client-sns": "^3.696.0", "@aws-sdk/client-sqs": "^3.658.1", "@smithy/node-http-handler": "^3.2.3", + "jszip": "^3.10.1", "node-fetch": "^2.7.0" } } diff --git a/contract-tests/images/applications/aws-sdk/server.js b/contract-tests/images/applications/aws-sdk/server.js index 6fd65c7..288bb96 100644 --- a/contract-tests/images/applications/aws-sdk/server.js +++ b/contract-tests/images/applications/aws-sdk/server.js @@ -5,6 +5,8 @@ const fs = require('fs'); const os = require('os'); const ospath = require('path'); const { NodeHttpHandler } =require('@smithy/node-http-handler'); +const fetch = require('node-fetch'); +const JSZip = require('jszip'); const { S3Client, CreateBucketCommand, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3'); const { DynamoDBClient, CreateTableCommand, PutItemCommand } = require('@aws-sdk/client-dynamodb'); @@ -14,6 +16,11 @@ const { BedrockClient, GetGuardrailCommand } = require('@aws-sdk/client-bedrock' const { BedrockAgentClient, GetKnowledgeBaseCommand, GetDataSourceCommand, GetAgentCommand } = require('@aws-sdk/client-bedrock-agent'); const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime'); const { BedrockAgentRuntimeClient, InvokeAgentCommand, RetrieveCommand } = require('@aws-sdk/client-bedrock-agent-runtime'); +const { SNSClient, CreateTopicCommand, GetTopicAttributesCommand } = require('@aws-sdk/client-sns'); +const { SecretsManagerClient, CreateSecretCommand, DescribeSecretCommand } = require('@aws-sdk/client-secrets-manager'); +const { SFNClient, CreateStateMachineCommand, CreateActivityCommand, DescribeStateMachineCommand, DescribeActivityCommand } = require('@aws-sdk/client-sfn'); +const { IAMClient, AttachRolePolicyCommand, CreateRoleCommand } = require('@aws-sdk/client-iam') +const { LambdaClient, CreateFunctionCommand, GetEventSourceMappingCommand, CreateEventSourceMappingCommand, UpdateEventSourceMappingCommand } = require('@aws-sdk/client-lambda'); const _PORT = 8080; @@ -25,6 +32,7 @@ const _AWS_SDK_ENDPOINT = process.env.AWS_SDK_ENDPOINT; const _AWS_REGION = process.env.AWS_REGION; const _FAULT_ENDPOINT = 'http://fault.test:8080'; + process.env.AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID || 'testcontainers-localstack'; process.env.AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY || 'testcontainers-localstack'; @@ -63,6 +71,16 @@ async function prepareAwsServer() { region: _AWS_REGION, }); + const secretsClient = new SecretsManagerClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }) + + const snsClient = new SNSClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }) + // Set up S3 await s3Client.send( new CreateBucketCommand({ @@ -102,7 +120,7 @@ async function prepareAwsServer() { ); // Set up SQS - await sqsClient.send( + const sqsQueue = await sqsClient.send( new CreateQueueCommand({ QueueName: 'test_put_get_queue', }) @@ -113,11 +131,32 @@ async function prepareAwsServer() { new CreateStreamCommand({ StreamName: 'test_stream', ShardCount: 1, + })) + + // Set up SecretsManager + await secretsClient.send( + new CreateSecretCommand({ + "Description": "My test secret", + "Name": "MyTestSecret", + "SecretString": "{\"username\":\"user\",\"password\":\"password\"}" }) ); + + // Set up SNS + await snsClient.send(new CreateTopicCommand({ + "Name": "TestTopic" + })) + + // Set up Lambda + await setupLambda() + + // Set up StepFunctions + await setupSfn() + } catch (error) { console.error('Unexpected exception occurred', error); } + } const server = http.createServer(async (req, res) => { @@ -134,7 +173,7 @@ const server = http.createServer(async (req, res) => { res.writeHead(405); res.end(); } -}); +}) async function handleGetRequest(req, res, path) { if (path.includes('s3')) { @@ -147,6 +186,14 @@ async function handleGetRequest(req, res, path) { await handleKinesisRequest(req, res, path); } else if (path.includes('bedrock')) { await handleBedrockRequest(req, res, path); + } else if (path.includes('secretsmanager')) { + await handleSecretsRequest(req, res, path); + } else if (path.includes('stepfunctions')) { + await handleSfnRequest(req, res, path); + } else if (path.includes('sns')) { + await handleSnsRequest(req, res, path); + } else if (path.includes('lambda')) { + await handleLambdaRequest(req, res, path); } else { res.writeHead(404); res.end(); @@ -517,7 +564,9 @@ async function handleBedrockRequest(req, res, path) { await withInjected200Success( bedrockClient, ['GetGuardrailCommand'], - { guardrailId: 'bt4o77i015cu' }, + { guardrailId: 'bt4o77i015cu', + guardrailArn: 'arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu' + }, async () => { await bedrockClient.send( new GetGuardrailCommand({ @@ -582,7 +631,29 @@ async function handleBedrockRequest(req, res, path) { }, ], } - + } + + if (path.includes("amazon.nova")) { + + modelId = "amazon.nova-pro-v1:0" + + request_body = { + messages: [{role: "user", content: [{text: "A camping trip"}]}], + inferenceConfig: { + max_new_tokens: 800, + temperature: 0.9, + top_p: 0.7, + }, + } + + response_body = { + output: {message: {content: [{text: ""}], role: "assistant"}}, + stopReason: "max_tokens", + usage: { + inputTokens: 432, + outputTokens: 681 + }, + } } if (path.includes('anthropic.claude')) { @@ -748,6 +819,349 @@ async function handleBedrockRequest(req, res, path) { res.end(); } + +async function handleSecretsRequest(req, res, path) { + const secretsClient = new SecretsManagerClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 400; + + try { + await secretsClient.send( + new DescribeSecretCommand({ + SecretId: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + const faultSecretsClient = new SecretsManagerClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultSecretsClient.send( + new DescribeSecretCommand({ + SecretId: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes('describesecret/my-secret')) { + await secretsClient.send( + new DescribeSecretCommand({ + SecretId: "MyTestSecret" + }) + ); + } + + res.end(); +} + +async function handleSfnRequest(req, res, path) { + const sfnClient = new SFNClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 400; + + try { + await sfnClient.send( + new DescribeStateMachineCommand({ + stateMachineArn: "arn:aws:states:us-west-2:000000000000:stateMachine:nonExistentStateMachine" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + + const faultSfnClient = new SFNClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultSfnClient.send( + new DescribeStateMachineCommand({ + stateMachineArn: "arn:aws:states:us-west-2:000000000000:stateMachine:invalid-state-machine" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes('describestatemachine/state-machine')) { + await sfnClient.send( + new DescribeStateMachineCommand({ + stateMachineArn: "arn:aws:states:us-west-2:000000000000:stateMachine:TestStateMachine" + }) + ); + } + + if (path.includes('describeactivity/activity')) { + await sfnClient.send( + new DescribeActivityCommand({ + activityArn: "arn:aws:states:us-west-2:000000000000:activity:TestActivity" + }) + ); + } + + res.end(); +} + +async function handleSnsRequest(req, res, path) { + const snsClient = new SNSClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 404; + + try { + await snsClient.send( + new GetTopicAttributesCommand({ + TopicArn: "arn:aws:sns:us-west-2:000000000000:nonExistentTopic", + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + const faultSnsClient = new SNSClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultSnsClient.send( + new GetTopicAttributesCommand({ + TopicArn: "arn:aws:sns:us-west-2:000000000000:invalidTopic" + }) + ); + } catch (err) { + console.log('Expected exception occurred', err); + } + } + + if (path.includes('gettopicattributes/topic')) { + await snsClient.send( + new GetTopicAttributesCommand({ + TopicArn: "arn:aws:sns:us-west-2:000000000000:TestTopic" + }) + ); + } + + res.end(); +} + +async function handleLambdaRequest(req, res, path) { + const lambdaClient = new LambdaClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + if (path.includes(_ERROR)) { + res.statusCode = 404; + + try { + await lambdaClient.send( + new GetEventSourceMappingCommand({ + UUID: "nonExistentUUID" + }) + ); + } + catch(err) { + console.log('Expected exception occurred', err); + } + + } + + if (path.includes(_FAULT)) { + res.statusCode = 500; + statusCodeForFault = 500; + + try { + const faultLambdaClient = new LambdaClient({ + endpoint: _FAULT_ENDPOINT, + region: _AWS_REGION, + ...noRetryConfig, + }); + + await faultLambdaClient.send( + new UpdateEventSourceMappingCommand({ + UUID: "123e4567-e89b-12d3-a456-426614174000" + }) + ); + } + catch(err) { + console.log('Expected exception occurred', err); + } + } + + + if (path.includes('geteventsourcemapping')) { + await lambdaClient.send( + new GetEventSourceMappingCommand({ + UUID: '' + }) + ); + } + res.end(); +} + +async function setupLambda() { + const lambdaClient = new LambdaClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const iamClient = new IAMClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const trustPolicy = { + Version: "2012-10-17", + Statement: [{ + Effect: "Allow", + Principal: { + Service: "lambda.amazonaws.com" + }, + Action: "sts:AssumeRole" + }] + }; + + const functionName = 'testFunction' + + const lambdaRoleParams = { + RoleName: "LambdaRole", + AssumeRolePolicyDocument: JSON.stringify(trustPolicy), + }; + + const policyParams = { + RoleName: "LambdaRole", + PolicyArn: "arn:aws:iam::aws:policy/AWSLambda_FullAccess" + }; + + const role = await iamClient.send(new CreateRoleCommand(lambdaRoleParams)); + await iamClient.send(new AttachRolePolicyCommand(policyParams)); + + const zip = new JSZip(); + zip.file('index.js', 'exports.handler = async (event) => { return { statusCode: 200 }; };'); + const zipBuffer = await zip.generateAsync({ type: 'nodebuffer' }); + + const functionParams = { + Code: { + ZipFile: zipBuffer + }, + FunctionName: functionName, + Handler: "index.handler", + Role: role.Role.Arn, + Runtime: "nodejs18.x" + }; + + const mappingParams = { + EventSourceArn: "arn:aws:sns:us-west-2:000000000000:TestTopic", + FunctionName: functionName, + Enabled: false + } + + await lambdaClient.send(new CreateFunctionCommand(functionParams)); + await lambdaClient.send(new CreateEventSourceMappingCommand(mappingParams)); +} + +async function setupSfn() { + const sfnClient = new SFNClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const iamClient = new IAMClient({ + endpoint: _AWS_SDK_ENDPOINT, + region: _AWS_REGION, + }); + + const trustPolicy = { + Version: "2012-10-17", + Statement: [{ + Effect: "Allow", + Principal: { + Service: "states.amazonaws.com" + }, + Action: "sts:AssumeRole" + }] +}; + +const roleName = 'testRole' + +const createRoleResponse = await iamClient.send(new CreateRoleCommand({ + RoleName: roleName, + AssumeRolePolicyDocument: JSON.stringify(trustPolicy), +})); + +await iamClient.send(new AttachRolePolicyCommand({ + RoleName: roleName, + PolicyArn: 'arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess' +})); + +const roleArn = createRoleResponse.Role.Arn + +const definition = { + StartAt: "HelloWorld", + States: { + "HelloWorld": { + Type: "Pass", + Result: "Hello, World!", + End: true + } + } +}; + +await sfnClient.send(new CreateStateMachineCommand({ + name: 'TestStateMachine', + definition: JSON.stringify(definition), + roleArn: roleArn, + type: 'STANDARD' +})); + +await sfnClient.send( + new CreateActivityCommand({ + name: 'TestActivity', + })); +} + function inject200Success(client, commandNames, additionalResponse = {}, middlewareName = 'inject200SuccessMiddleware') { const middleware = (next, context) => async (args) => { const { commandName } = context; @@ -775,12 +1189,9 @@ async function withInjected200Success(client, commandNames, additionalResponse, client.middlewareStack.remove(middlewareName); } - - prepareAwsServer().then(() => { server.listen(_PORT, '0.0.0.0', () => { console.log('Server is listening on port', _PORT); console.log('Ready'); }); -}); - +}); \ No newline at end of file diff --git a/contract-tests/images/applications/mysql2/package.json b/contract-tests/images/applications/mysql2/package.json index 0c1fe64..5a18ef9 100644 --- a/contract-tests/images/applications/mysql2/package.json +++ b/contract-tests/images/applications/mysql2/package.json @@ -12,4 +12,4 @@ "dependencies": { "mysql2": "3.11.4" } -} +} \ No newline at end of file diff --git a/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py b/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py index 31f72d2..005e286 100644 --- a/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py +++ b/contract-tests/tests/test/amazon/aws-sdk/aws_sdk_test.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 from logging import INFO, Logger, getLogger import math +import re from typing import Dict, List from docker.types import EndpointConfig @@ -18,6 +19,7 @@ AWS_REMOTE_RESOURCE_TYPE, AWS_REMOTE_SERVICE, AWS_SPAN_KIND, + AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER ) from opentelemetry.proto.common.v1.common_pb2 import AnyValue, KeyValue from opentelemetry.proto.metrics.v1.metrics_pb2 import ExponentialHistogramDataPoint, Metric @@ -32,8 +34,14 @@ _AWS_KINESIS_STREAM_NAME: str = "aws.kinesis.stream.name" _AWS_BEDROCK_AGENT_ID: str = "aws.bedrock.agent.id" _AWS_BEDROCK_GUARDRAIL_ID: str = "aws.bedrock.guardrail.id" +_AWS_BEDROCK_GUARDRAIL_ARN: str = "aws.bedrock.guardrail.arn" _AWS_BEDROCK_KNOWLEDGE_BASE_ID: str = "aws.bedrock.knowledge_base.id" _AWS_BEDROCK_DATA_SOURCE_ID: str = "aws.bedrock.data_source.id" +_AWS_SECRET_ARN: str = "aws.secretsmanager.secret.arn" +_AWS_SNS_TOPIC_ARN: str = 'aws.sns.topic.arn' +_AWS_LAMBDA_RESOURCE_MAPPING_ID: str = 'aws.lambda.resource_mapping.id' +_AWS_STATE_MACHINE_ARN: str = "aws.stepfunctions.state_machine.arn" +_AWS_ACTIVITY_ARN: str = "aws.stepfunctions.activity.arn" _GEN_AI_REQUEST_MODEL: str = "gen_ai.request.model" _GEN_AI_REQUEST_TEMPERATURE: str = "gen_ai.request.temperature" _GEN_AI_REQUEST_TOP_P: str = "gen_ai.request.top_p" @@ -77,8 +85,9 @@ def set_up_dependency_container(cls): cls._local_stack: LocalStackContainer = ( LocalStackContainer(image="localstack/localstack:3.5.0") .with_name("localstack") - .with_services("s3", "sqs", "dynamodb", "kinesis") + .with_services("s3", "sqs", "dynamodb", "kinesis", 'secretsmanager', 'stepfunctions', 'iam', 'sns', "lambda") .with_env("DEFAULT_REGION", "us-west-2") + .with_volume_mapping("/var/run/docker.sock", "/var/run/docker.sock") .with_kwargs(network=NETWORK_NAME, networking_config=local_stack_networking_config) ) cls._local_stack.start() @@ -104,6 +113,7 @@ def test_s3_create_bucket(self): remote_operation="CreateBucket", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="test-bucket-name", + cloudformation_primary_identifier="test-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "test-bucket-name", }, @@ -122,6 +132,7 @@ def test_s3_create_object(self): remote_operation="PutObject", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="test-put-object-bucket-name", + cloudformation_primary_identifier="test-put-object-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "test-put-object-bucket-name", }, @@ -140,6 +151,7 @@ def test_s3_get_object(self): remote_operation="GetObject", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="test-get-object-bucket-name", + cloudformation_primary_identifier="test-get-object-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "test-get-object-bucket-name", }, @@ -158,6 +170,7 @@ def test_s3_error(self): remote_operation="CreateBucket", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="-", + cloudformation_primary_identifier="-", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "-", }, @@ -178,6 +191,7 @@ def test_s3_fault(self): remote_operation="CreateBucket", remote_resource_type="AWS::S3::Bucket", remote_resource_identifier="valid-bucket-name", + cloudformation_primary_identifier="valid-bucket-name", request_specific_attributes={ SpanAttributes.AWS_S3_BUCKET: "valid-bucket-name", }, @@ -196,6 +210,7 @@ def test_dynamodb_create_table(self): remote_operation="CreateTable", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="test_table", + cloudformation_primary_identifier="test_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["test_table"], }, @@ -214,6 +229,7 @@ def test_dynamodb_put_item(self): remote_operation="PutItem", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="put_test_table", + cloudformation_primary_identifier="put_test_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["put_test_table"], }, @@ -232,6 +248,7 @@ def test_dynamodb_error(self): remote_operation="PutItem", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="invalid_table", + cloudformation_primary_identifier="invalid_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["invalid_table"], }, @@ -252,6 +269,7 @@ def test_dynamodb_fault(self): remote_operation="PutItem", remote_resource_type="AWS::DynamoDB::Table", remote_resource_identifier="invalid_table", + cloudformation_primary_identifier="invalid_table", request_specific_attributes={ SpanAttributes.AWS_DYNAMODB_TABLE_NAMES: ["invalid_table"], }, @@ -270,6 +288,7 @@ def test_sqs_create_queue(self): remote_operation="CreateQueue", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="test_queue", + cloudformation_primary_identifier="test_queue", request_specific_attributes={ _AWS_SQS_QUEUE_NAME: "test_queue", }, @@ -289,6 +308,7 @@ def test_sqs_send_message(self): remote_operation="SendMessage", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="test_put_get_queue", + cloudformation_primary_identifier="http://localstack:4566/000000000000/test_put_get_queue", request_specific_attributes={ _AWS_SQS_QUEUE_URL: "http://localstack:4566/000000000000/test_put_get_queue", }, @@ -309,6 +329,7 @@ def test_sqs_receive_message(self): remote_operation="ReceiveMessage", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="test_put_get_queue", + cloudformation_primary_identifier="http://localstack:4566/000000000000/test_put_get_queue", request_specific_attributes={ _AWS_SQS_QUEUE_URL: "http://localstack:4566/000000000000/test_put_get_queue", }, @@ -329,6 +350,7 @@ def test_sqs_error(self): remote_operation="SendMessage", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="sqserror", + cloudformation_primary_identifier="http://error.test:8080/000000000000/sqserror", request_specific_attributes={ _AWS_SQS_QUEUE_URL: "http://error.test:8080/000000000000/sqserror", }, @@ -350,6 +372,7 @@ def test_sqs_fault(self): remote_operation="CreateQueue", remote_resource_type="AWS::SQS::Queue", remote_resource_identifier="invalid_test", + cloudformation_primary_identifier="invalid_test", request_specific_attributes={ _AWS_SQS_QUEUE_NAME: "invalid_test", }, @@ -368,6 +391,7 @@ def test_kinesis_put_record(self): remote_operation="PutRecord", remote_resource_type="AWS::Kinesis::Stream", remote_resource_identifier="test_stream", + cloudformation_primary_identifier="test_stream", request_specific_attributes={ _AWS_KINESIS_STREAM_NAME: "test_stream", }, @@ -386,6 +410,7 @@ def test_kinesis_error(self): remote_operation="PutRecord", remote_resource_type="AWS::Kinesis::Stream", remote_resource_identifier="invalid_stream", + cloudformation_primary_identifier="invalid_stream", request_specific_attributes={ _AWS_KINESIS_STREAM_NAME: "invalid_stream", }, @@ -406,6 +431,7 @@ def test_kinesis_fault(self): remote_operation="PutRecord", remote_resource_type="AWS::Kinesis::Stream", remote_resource_identifier="test_stream", + cloudformation_primary_identifier="test_stream", request_specific_attributes={ _AWS_KINESIS_STREAM_NAME: "test_stream", }, @@ -425,6 +451,7 @@ def test_bedrock_runtime_invoke_model_amazon_titan(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='amazon.titan-text-premier-v1:0', + cloudformation_primary_identifier="amazon.titan-text-premier-v1:0", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'amazon.titan-text-premier-v1:0', _GEN_AI_REQUEST_MAX_TOKENS: 3072, @@ -439,6 +466,35 @@ def test_bedrock_runtime_invoke_model_amazon_titan(self): span_name="BedrockRuntime.InvokeModel" ) + + def test_bedrock_runtime_invoke_model_amazon_nova(self): + result = self.do_test_requests( + "bedrock/invokemodel/invoke-model/amazon.nova-pro-v1:0", + "GET", + 200, + 0, + 0, + local_operation="GET /bedrock", + rpc_service="BedrockRuntime", + remote_service="AWS::BedrockRuntime", + remote_operation="InvokeModel", + remote_resource_type="AWS::Bedrock::Model", + remote_resource_identifier='amazon.nova-pro-v1:0', + cloudformation_primary_identifier="amazon.nova-pro-v1:0", + request_specific_attributes={ + _GEN_AI_REQUEST_MODEL: 'amazon.nova-pro-v1:0', + _GEN_AI_REQUEST_MAX_TOKENS: 800, + _GEN_AI_REQUEST_TEMPERATURE: 0.9, + _GEN_AI_REQUEST_TOP_P: 0.7 + }, + response_specific_attributes={ + _GEN_AI_RESPONSE_FINISH_REASONS: ['max_tokens'], + _GEN_AI_USAGE_INPUT_TOKENS: 432, + _GEN_AI_USAGE_OUTPUT_TOKENS: 681 + }, + + span_name="BedrockRuntime.InvokeModel" + ) def test_bedrock_runtime_invoke_model_anthropic_claude(self): self.do_test_requests( @@ -453,6 +509,7 @@ def test_bedrock_runtime_invoke_model_anthropic_claude(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='anthropic.claude-v2:1', + cloudformation_primary_identifier="anthropic.claude-v2:1", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'anthropic.claude-v2:1', _GEN_AI_REQUEST_MAX_TOKENS: 1000, @@ -480,6 +537,7 @@ def test_bedrock_runtime_invoke_model_meta_llama(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='meta.llama2-13b-chat-v1', + cloudformation_primary_identifier="meta.llama2-13b-chat-v1", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'meta.llama2-13b-chat-v1', _GEN_AI_REQUEST_MAX_TOKENS: 512, @@ -507,6 +565,7 @@ def test_bedrock_runtime_invoke_model_cohere_command_r(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='cohere.command-r-v1:0', + cloudformation_primary_identifier="cohere.command-r-v1:0", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'cohere.command-r-v1:0', _GEN_AI_REQUEST_MAX_TOKENS: 512, @@ -535,6 +594,7 @@ def test_bedrock_runtime_invoke_model_cohere_command(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='cohere.command-light-text-v14', + cloudformation_primary_identifier="cohere.command-light-text-v14", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'cohere.command-light-text-v14', _GEN_AI_REQUEST_MAX_TOKENS: 512, @@ -562,6 +622,7 @@ def test_bedrock_runtime_invoke_model_ai21_jamba(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='ai21.jamba-1-5-large-v1:0', + cloudformation_primary_identifier="ai21.jamba-1-5-large-v1:0", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'ai21.jamba-1-5-large-v1:0', _GEN_AI_REQUEST_MAX_TOKENS: 512, @@ -589,6 +650,7 @@ def test_bedrock_runtime_invoke_model_mistral_mistral(self): remote_operation="InvokeModel", remote_resource_type="AWS::Bedrock::Model", remote_resource_identifier='mistral.mistral-7b-instruct-v0:2', + cloudformation_primary_identifier="mistral.mistral-7b-instruct-v0:2", request_specific_attributes={ _GEN_AI_REQUEST_MODEL: 'mistral.mistral-7b-instruct-v0:2', _GEN_AI_REQUEST_MAX_TOKENS: 4096, @@ -616,8 +678,10 @@ def test_bedrock_get_guardrail(self): remote_operation="GetGuardrail", remote_resource_type="AWS::Bedrock::Guardrail", remote_resource_identifier="bt4o77i015cu", + cloudformation_primary_identifier="arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu", request_specific_attributes={ _AWS_BEDROCK_GUARDRAIL_ID: "bt4o77i015cu", + _AWS_BEDROCK_GUARDRAIL_ARN: "arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu" }, span_name="Bedrock.GetGuardrail", ) @@ -635,6 +699,7 @@ def test_bedrock_agent_runtime_invoke_agent(self): remote_operation="InvokeAgent", remote_resource_type="AWS::Bedrock::Agent", remote_resource_identifier="Q08WFRPHVL", + cloudformation_primary_identifier="Q08WFRPHVL", request_specific_attributes={ _AWS_BEDROCK_AGENT_ID: "Q08WFRPHVL", }, @@ -654,6 +719,7 @@ def test_bedrock_agent_runtime_retrieve(self): remote_operation="Retrieve", remote_resource_type="AWS::Bedrock::KnowledgeBase", remote_resource_identifier="test-knowledge-base-id", + cloudformation_primary_identifier="test-knowledge-base-id", request_specific_attributes={ _AWS_BEDROCK_KNOWLEDGE_BASE_ID: "test-knowledge-base-id", }, @@ -673,6 +739,7 @@ def test_bedrock_agent_get_agent(self): remote_operation="GetAgent", remote_resource_type="AWS::Bedrock::Agent", remote_resource_identifier="TESTAGENTID", + cloudformation_primary_identifier="TESTAGENTID", request_specific_attributes={ _AWS_BEDROCK_AGENT_ID: "TESTAGENTID", }, @@ -692,6 +759,7 @@ def test_bedrock_agent_get_knowledge_base(self): remote_operation="GetKnowledgeBase", remote_resource_type="AWS::Bedrock::KnowledgeBase", remote_resource_identifier="invalid-knowledge-base-id", + cloudformation_primary_identifier="invalid-knowledge-base-id", request_specific_attributes={ _AWS_BEDROCK_KNOWLEDGE_BASE_ID: "invalid-knowledge-base-id", }, @@ -711,12 +779,263 @@ def test_bedrock_agent_get_data_source(self): remote_operation="GetDataSource", remote_resource_type="AWS::Bedrock::DataSource", remote_resource_identifier="DATASURCID", + cloudformation_primary_identifier=r'TESTKBSEID\|DATASURCID', request_specific_attributes={ _AWS_BEDROCK_DATA_SOURCE_ID: "DATASURCID", }, span_name="BedrockAgent.GetDataSource", ) + def test_secretsmanager_fault(self): + self.do_test_requests( + "secretsmanager/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /secretsmanager", + local_operation_2="POST /", + rpc_service="SecretsManager", + remote_service="AWS::SecretsManager", + remote_operation="DescribeSecret", + remote_resource_type="AWS::SecretsManager::Secret", + remote_resource_identifier="nonExistentSecret", + cloudformation_primary_identifier="arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + request_specific_attributes= { + _AWS_SECRET_ARN: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + }, + span_name="SecretsManager.DescribeSecret", + ) + + def test_secretsmanager_error(self): + self.do_test_requests( + "secretsmanager/error", + "GET", + 400, + 1, + 0, + local_operation="GET /secretsmanager", + rpc_service="SecretsManager", + remote_service="AWS::SecretsManager", + remote_operation="DescribeSecret", + remote_resource_type="AWS::SecretsManager::Secret", + remote_resource_identifier="nonExistentSecret", + cloudformation_primary_identifier="arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + request_specific_attributes= { + _AWS_SECRET_ARN: "arn:aws:secretsmanager:us-west-2:000000000000:secret:nonExistentSecret", + }, + span_name="SecretsManager.DescribeSecret", + ) + + def test_secretsmanager_describe_secret(self): + self.do_test_requests( + "secretsmanager/describesecret/my-secret", + "GET", + 200, + 0, + 0, + local_operation="GET /secretsmanager", + rpc_service="SecretsManager", + remote_service="AWS::SecretsManager", + remote_operation="DescribeSecret", + remote_resource_type="AWS::SecretsManager::Secret", + remote_resource_identifier=r'MyTestSecret-[a-zA-Z0-9]{6}$', + cloudformation_primary_identifier=r'arn:aws:secretsmanager:us-west-2:000000000000:secret:MyTestSecret-[a-zA-Z0-9]{6}$', + response_specific_attributes= { + _AWS_SECRET_ARN: r'arn:aws:secretsmanager:us-west-2:000000000000:secret:MyTestSecret-[a-zA-Z0-9]{6}$', + }, + span_name="SecretsManager.DescribeSecret", + ) + + def test_stepfunctions_fault(self): + self.do_test_requests( + "stepfunctions/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /stepfunctions", + local_operation_2="POST /", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeStateMachine", + remote_resource_type="AWS::StepFunctions::StateMachine", + remote_resource_identifier="invalid-state-machine", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:stateMachine:invalid-state-machine", + request_specific_attributes= { + _AWS_STATE_MACHINE_ARN: "arn:aws:states:us-west-2:000000000000:stateMachine:invalid-state-machine", + }, + span_name="SFN.DescribeStateMachine", + ) + + def test_stepfunctions_error(self): + self.do_test_requests( + "stepfunctions/error", + "GET", + 400, + 1, + 0, + local_operation="GET /stepfunctions", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeStateMachine", + remote_resource_type="AWS::StepFunctions::StateMachine", + remote_resource_identifier="nonExistentStateMachine", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:stateMachine:nonExistentStateMachine", + request_specific_attributes= { + _AWS_STATE_MACHINE_ARN: "arn:aws:states:us-west-2:000000000000:stateMachine:nonExistentStateMachine", + }, + span_name="SFN.DescribeStateMachine", + ) + + def test_stepfunctions_describe_state_machine(self): + self.do_test_requests( + "stepfunctions/describestatemachine/state-machine", + "GET", + 200, + 0, + 0, + local_operation="GET /stepfunctions", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeStateMachine", + remote_resource_type="AWS::StepFunctions::StateMachine", + remote_resource_identifier="TestStateMachine", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:stateMachine:TestStateMachine", + request_specific_attributes= { + _AWS_STATE_MACHINE_ARN: "arn:aws:states:us-west-2:000000000000:stateMachine:TestStateMachine", + }, + span_name="SFN.DescribeStateMachine", + ) + + def test_stepfunctions_describe_activity(self): + self.do_test_requests( + "stepfunctions/describeactivity/activity", + "GET", + 200, + 0, + 0, + local_operation="GET /stepfunctions", + rpc_service="SFN", + remote_service="AWS::StepFunctions", + remote_operation="DescribeActivity", + remote_resource_type="AWS::StepFunctions::Activity", + remote_resource_identifier="TestActivity", + cloudformation_primary_identifier="arn:aws:states:us-west-2:000000000000:activity:TestActivity", + request_specific_attributes= { + _AWS_ACTIVITY_ARN: "arn:aws:states:us-west-2:000000000000:activity:TestActivity", + }, + span_name="SFN.DescribeActivity", + ) + + def test_sns_fault(self): + self.do_test_requests( + "sns/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /sns", + local_operation_2="POST /", + rpc_service="SNS", + remote_service="AWS::SNS", + remote_operation="GetTopicAttributes", + remote_resource_type="AWS::SNS::Topic", + remote_resource_identifier="invalidTopic", + cloudformation_primary_identifier="arn:aws:sns:us-west-2:000000000000:invalidTopic", + request_specific_attributes= { + _AWS_SNS_TOPIC_ARN: "arn:aws:sns:us-west-2:000000000000:invalidTopic", + }, + span_name="SNS GetTopicAttributes", + ) + + def test_sns_error(self): + self.do_test_requests( + "sns/error", + "GET", + 404, # this is the expected status code error for sns + 1, + 0, + local_operation="GET /sns", + rpc_service="SNS", + remote_service="AWS::SNS", + remote_operation="GetTopicAttributes", + remote_resource_type="AWS::SNS::Topic", + remote_resource_identifier="nonExistentTopic", + cloudformation_primary_identifier="arn:aws:sns:us-west-2:000000000000:nonExistentTopic", + request_specific_attributes= { + _AWS_SNS_TOPIC_ARN: "arn:aws:sns:us-west-2:000000000000:nonExistentTopic", + }, + span_name="SNS GetTopicAttributes", + ) + + def test_sns_get_topic_attributes(self): + self.do_test_requests( + "sns/gettopicattributes/topic", + "GET", + 200, + 0, + 0, + local_operation="GET /sns", + rpc_service="SNS", + remote_service="AWS::SNS", + remote_operation="GetTopicAttributes", + remote_resource_type="AWS::SNS::Topic", + remote_resource_identifier="TestTopic", + cloudformation_primary_identifier="arn:aws:sns:us-west-2:000000000000:TestTopic", + request_specific_attributes= { + _AWS_SNS_TOPIC_ARN: "arn:aws:sns:us-west-2:000000000000:TestTopic", + }, + span_name="SNS GetTopicAttributes", + ) + + def test_lambda_fault(self): + self.do_test_requests( + "lambda/fault", + "GET", + 500, + 0, + 1, + dp_count=3, + local_operation="GET /lambda", + local_operation_2="PUT /2015-03-31", + rpc_service="Lambda", + remote_service="AWS::Lambda", + remote_operation="UpdateEventSourceMapping", + remote_resource_type="AWS::Lambda::EventSourceMapping", + remote_resource_identifier="123e4567-e89b-12d3-a456-426614174000", + cloudformation_primary_identifier="123e4567-e89b-12d3-a456-426614174000", + request_specific_attributes= { + _AWS_LAMBDA_RESOURCE_MAPPING_ID: "123e4567-e89b-12d3-a456-426614174000", + }, + span_name="Lambda.UpdateEventSourceMapping", + ) + + def test_lambda_error(self): + self.do_test_requests( + "lambda/error", + "GET", + 404, + 1, + 0, + local_operation="GET /lambda", + rpc_service="Lambda", + remote_service="AWS::Lambda", + remote_operation="GetEventSourceMapping", + remote_resource_type="AWS::Lambda::EventSourceMapping", + remote_resource_identifier="nonExistentUUID", + cloudformation_primary_identifier="nonExistentUUID", + request_specific_attributes= { + _AWS_LAMBDA_RESOURCE_MAPPING_ID: "nonExistentUUID", + }, + span_name="Lambda.GetEventSourceMapping", + ) + + #TODO: Need to add test_lambda_get_event_source_mapping once workaround is figured out for storing UUID between tests + @override def _assert_aws_span_attributes(self, resource_scope_spans: List[ResourceScopeSpan], path: str, **kwargs) -> None: target_spans: List[Span] = [] @@ -742,6 +1061,7 @@ def _assert_aws_span_attributes(self, resource_scope_spans: List[ResourceScopeSp span_kind, kwargs.get("remote_resource_type", "None"), kwargs.get("remote_resource_identifier", "None"), + kwargs.get("cloudformation_primary_identifier", "None"), ) def _assert_aws_attributes( @@ -753,6 +1073,7 @@ def _assert_aws_attributes( span_kind: str, remote_resource_type: str, remote_resource_identifier: str, + cloudformation_primary_identifier: str ) -> None: attributes_dict: Dict[str, AnyValue] = self._get_attributes_dict(attributes_list) self._assert_str_attribute(attributes_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) @@ -760,16 +1081,19 @@ def _assert_aws_attributes( self._assert_str_attribute(attributes_dict, AWS_REMOTE_SERVICE, remote_service) self._assert_str_attribute(attributes_dict, AWS_REMOTE_OPERATION, remote_operation) if remote_resource_type != "None": - self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) + self._assert_attribute(attributes_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) if remote_resource_identifier != "None": - self._assert_str_attribute(attributes_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + self._assert_attribute(attributes_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + if cloudformation_primary_identifier != "None": + self._assert_attribute(attributes_dict, AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER, cloudformation_primary_identifier) + self._assert_str_attribute(attributes_dict, AWS_SPAN_KIND, span_kind) @override def _assert_semantic_conventions_span_attributes( self, resource_scope_spans: List[ResourceScopeSpan], method: str, path: str, status_code: int, **kwargs ) -> None: - + target_spans: List[Span] = [] for resource_scope_span in resource_scope_spans: # pylint: disable=no-member @@ -786,7 +1110,7 @@ def _assert_semantic_conventions_span_attributes( status_code, kwargs.get("request_specific_attributes", {}), kwargs.get("response_specific_attributes", {}), - ) + ) # pylint: disable=unidiomatic-typecheck def _assert_semantic_conventions_attributes( @@ -805,19 +1129,30 @@ def _assert_semantic_conventions_attributes( self._assert_int_attribute(attributes_dict, SpanAttributes.HTTP_STATUS_CODE, status_code) # TODO: aws sdk instrumentation is not respecting PEER_SERVICE # self._assert_str_attribute(attributes_dict, SpanAttributes.PEER_SERVICE, "backend:8080") - self._assert_specific_attributes(attributes_dict, request_specific_attributes) - self._assert_specific_attributes(attributes_dict, response_specific_attributes) + for key, value in request_specific_attributes.items(): + self._assert_attribute(attributes_dict, key, value) + + for key, value in response_specific_attributes.items(): + self._assert_attribute(attributes_dict, key, value) - def _assert_specific_attributes(self, attributes_dict: Dict[str, AnyValue], specific_attributes: Dict[str, AnyValue]) -> None: - for key, value in specific_attributes.items(): - if isinstance(value, str): - self._assert_str_attribute(attributes_dict, key, value) - elif isinstance(value, int): - self._assert_int_attribute(attributes_dict, key, value) - elif isinstance(value, float): - self._assert_float_attribute(attributes_dict, key, value) - else: - self._assert_array_value_ddb_table_name(attributes_dict, key, value) + def _assert_attribute(self, attributes_dict: Dict[str, AnyValue], key, value) -> None: + if isinstance(value, str): + self._assert_str_attribute(attributes_dict, key, value) + elif isinstance(value, int): + self._assert_int_attribute(attributes_dict, key, value) + elif isinstance(value, float): + self._assert_float_attribute(attributes_dict, key, value) + else: + self._assert_array_value_ddb_table_name(attributes_dict, key, value) + + @override + def _assert_str_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, expected_value: str): + self.assertIn(key, attributes_dict) + actual_value: AnyValue = attributes_dict[key] + self.assertIsNotNone(actual_value) + pattern = re.compile(expected_value) + match = pattern.fullmatch(actual_value.string_value) + self.assertTrue(match is not None, f"Actual: {actual_value.string_value} does not match Expected: {expected_value}") @override def _assert_metric_attributes( @@ -845,23 +1180,23 @@ def _assert_metric_attributes( dependency_dp = dp_list[1] service_dp = dp_list[0] attribute_dict: Dict[str, AnyValue] = self._get_attributes_dict(dependency_dp.attributes) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) - self._assert_str_attribute(attribute_dict, AWS_REMOTE_SERVICE, kwargs.get("remote_service")) - self._assert_str_attribute(attribute_dict, AWS_REMOTE_OPERATION, kwargs.get("remote_operation")) - self._assert_str_attribute(attribute_dict, AWS_SPAN_KIND, kwargs.get("dependency_metric_span_kind") or "CLIENT") + self._assert_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) + self._assert_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) + self._assert_attribute(attribute_dict, AWS_REMOTE_SERVICE, kwargs.get("remote_service")) + self._assert_attribute(attribute_dict, AWS_REMOTE_OPERATION, kwargs.get("remote_operation")) + self._assert_attribute(attribute_dict, AWS_SPAN_KIND, kwargs.get("dependency_metric_span_kind") or "CLIENT") remote_resource_type = kwargs.get("remote_resource_type", "None") remote_resource_identifier = kwargs.get("remote_resource_identifier", "None") if remote_resource_type != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) if remote_resource_identifier != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) self.check_sum(metric_name, dependency_dp.sum, expected_sum) attribute_dict: Dict[str, AnyValue] = self._get_attributes_dict(service_dp.attributes) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) - self._assert_str_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) - self._assert_str_attribute(attribute_dict, AWS_SPAN_KIND, "LOCAL_ROOT") + self._assert_attribute(attribute_dict, AWS_LOCAL_SERVICE, self.get_application_otel_service_name()) + self._assert_attribute(attribute_dict, AWS_LOCAL_OPERATION, kwargs.get("local_operation")) + self._assert_attribute(attribute_dict, AWS_SPAN_KIND, "LOCAL_ROOT") self.check_sum(metric_name, service_dp.sum, expected_sum) else: dependency_dp: ExponentialHistogramDataPoint = max(dp_list, key=lambda dp: len(dp.attributes)) @@ -878,9 +1213,9 @@ def _assert_metric_attributes( remote_resource_type = kwargs.get("remote_resource_type", "None") remote_resource_identifier = kwargs.get("remote_resource_identifier", "None") if remote_resource_type != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_TYPE, remote_resource_type) if remote_resource_identifier != "None": - self._assert_str_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) + self._assert_attribute(attribute_dict, AWS_REMOTE_RESOURCE_IDENTIFIER, remote_resource_identifier) self.check_sum(metric_name, dependency_dp.sum, expected_sum) attribute_dict_service: Dict[str, AnyValue] = self._get_attributes_dict(service_dp.attributes) @@ -902,10 +1237,29 @@ def _assert_metric_attributes( self._assert_str_attribute(attribute_dict_other, AWS_SPAN_KIND, "LOCAL_ROOT") self.check_sum(metric_name, other_dp.sum, expected_sum) + def _assert_attribute(self, attributes_dict: Dict[str, AnyValue], key, value) -> None: + if isinstance(value, str): + self._assert_str_attribute(attributes_dict, key, value) + elif isinstance(value, int): + self._assert_int_attribute(attributes_dict, key, value) + elif isinstance(value, float): + self._assert_float_attribute(attributes_dict, key, value) + else: + self._assert_array_value_ddb_table_name(attributes_dict, key, value) + + @override + def _assert_str_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, expected_value: str): + self.assertIn(key, attributes_dict) + actual_value: AnyValue = attributes_dict[key] + self.assertIsNotNone(actual_value) + pattern = re.compile(expected_value) + match = pattern.fullmatch(actual_value.string_value) + self.assertTrue(match is not None, f"Actual: {actual_value.string_value} does not match Expected: {expected_value}") + # pylint: disable=consider-using-enumerate def _assert_array_value_ddb_table_name(self, attributes_dict: Dict[str, AnyValue], key: str, expect_values: list): self.assertIn(key, attributes_dict) actual_values: [AnyValue] = attributes_dict[key].array_value self.assertEqual(len(actual_values.values), len(expect_values)) for index in range(len(actual_values.values)): - self.assertEqual(actual_values.values[index].string_value, expect_values[index]) + self.assertEqual(actual_values.values[index].string_value, expect_values[index]) \ No newline at end of file diff --git a/contract-tests/tests/test/amazon/base/contract_test_base.py b/contract-tests/tests/test/amazon/base/contract_test_base.py index ea522f8..07a59ab 100644 --- a/contract-tests/tests/test/amazon/base/contract_test_base.py +++ b/contract-tests/tests/test/amazon/base/contract_test_base.py @@ -1,6 +1,7 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 import time +import re from logging import INFO, Logger, getLogger from typing import Dict, List from unittest import TestCase @@ -229,7 +230,7 @@ def _assert_int_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, actual_value: AnyValue = attributes_dict[key] self.assertIsNotNone(actual_value) self.assertEqual(expected_value, actual_value.int_value) - + def _assert_float_attribute(self, attributes_dict: Dict[str, AnyValue], key: str, expected_value: float) -> None: self.assertIn(key, attributes_dict) actual_value: AnyValue = attributes_dict[key] @@ -285,4 +286,4 @@ def _assert_semantic_conventions_span_attributes( def _assert_metric_attributes( self, resource_scope_metrics: List[ResourceScopeMetric], metric_name: str, expected_sum: int, **kwargs ): - self.fail("Tests must implement this function") + self.fail("Tests must implement this function") \ No newline at end of file diff --git a/contract-tests/tests/test/amazon/utils/application_signals_constants.py b/contract-tests/tests/test/amazon/utils/application_signals_constants.py index 9f3a625..14b602e 100644 --- a/contract-tests/tests/test/amazon/utils/application_signals_constants.py +++ b/contract-tests/tests/test/amazon/utils/application_signals_constants.py @@ -17,4 +17,5 @@ AWS_REMOTE_OPERATION: str = "aws.remote.operation" AWS_REMOTE_RESOURCE_TYPE: str = "aws.remote.resource.type" AWS_REMOTE_RESOURCE_IDENTIFIER: str = "aws.remote.resource.identifier" +AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: str = 'aws.remote.resource.cfn.primary.identifier' AWS_SPAN_KIND: str = "aws.span.kind" diff --git a/package-lock.json b/package-lock.json index 97ce276..7d8f801 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,11 @@ "@aws-sdk/client-bedrock-agent-runtime": "3.632.0", "@aws-sdk/client-bedrock-runtime": "3.632.0", "@aws-sdk/client-kinesis": "3.632.0", + "@aws-sdk/client-lambda": "^3.632.0", "@aws-sdk/client-s3": "3.632.0", + "@aws-sdk/client-secrets-manager": "^3.632.0", + "@aws-sdk/client-sfn": "^3.632.0", + "@aws-sdk/client-sns": "^3.632.0", "@opentelemetry/contrib-test-utils": "0.41.0", "@types/mocha": "7.0.2", "@types/node": "18.6.5", @@ -613,48 +617,782 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-lambda": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.699.0.tgz", + "integrity": "sha512-K9TGvQB8hkjwNhfWSfYllUpttqxTcd78ShSRCIhlcwzzsmQphET10xEb0Tm1k8sqriSQ+CiVOFSkX78gqoHzBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.699.0", + "@aws-sdk/client-sts": "3.699.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.699.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@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-node": "^3.0.10", + "@smithy/invalid-dependency": "^3.0.10", + "@smithy/middleware-content-length": "^3.0.12", + "@smithy/middleware-endpoint": "^3.2.3", + "@smithy/middleware-retry": "^3.0.27", + "@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.4", + "@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.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@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.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.696.0.tgz", + "integrity": "sha512-q5TTkd08JS0DOkHfUL853tuArf7NrPeqoS5UOvqJho8ibV9Ak/a/HO4kNvy9Nj3cib/toHYHsQIEtecUPSUUrQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@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.3", + "@smithy/middleware-retry": "^3.0.27", + "@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.4", + "@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.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@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.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.699.0.tgz", + "integrity": "sha512-u8a1GorY5D1l+4FQAf4XBUC1T10/t7neuwT21r0ymrtMFSK2a9QqVHKMoLkvavAwyhJnARSBM9/UQC797PFOFw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.699.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@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.3", + "@smithy/middleware-retry": "^3.0.27", + "@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.4", + "@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.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@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.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.699.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/client-sts": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.699.0.tgz", + "integrity": "sha512-++lsn4x2YXsZPIzFVwv3fSUVM55ZT0WRFmPeNilYIhZClxHLmVAWKH4I55cY9ry60/aTKYjzOXkWwyBKGsGvQg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.699.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-node": "3.699.0", + "@aws-sdk/middleware-host-header": "3.696.0", + "@aws-sdk/middleware-logger": "3.696.0", + "@aws-sdk/middleware-recursion-detection": "3.696.0", + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/region-config-resolver": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@aws-sdk/util-user-agent-browser": "3.696.0", + "@aws-sdk/util-user-agent-node": "3.696.0", + "@smithy/config-resolver": "^3.0.12", + "@smithy/core": "^2.5.3", + "@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.3", + "@smithy/middleware-retry": "^3.0.27", + "@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.4", + "@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.27", + "@smithy/util-defaults-mode-node": "^3.0.27", + "@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.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/core": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.696.0.tgz", + "integrity": "sha512-3c9III1k03DgvRZWg8vhVmfIXPG6hAciN9MzQTzqGngzWAELZF/WONRTRQuDFixVtarQatmLHYVw/atGeA2Byw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/core": "^2.5.3", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.7", + "@smithy/signature-v4": "^4.2.2", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-middleware": "^3.0.10", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.696.0.tgz", + "integrity": "sha512-T9iMFnJL7YTlESLpVFT3fg1Lkb1lD+oiaIC8KMpepb01gDUBIpj9+Y+pA/cgRWW0yRxmkDXNazAE2qQTVFGJzA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.696.0.tgz", + "integrity": "sha512-GV6EbvPi2eq1+WgY/o2RFA3P7HGmnkIzCNmhwtALFlqMroLYWKE7PSeHw66Uh1dFQeVESn0/+hiUNhu1mB0emA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/fetch-http-handler": "^4.1.1", + "@smithy/node-http-handler": "^3.3.1", + "@smithy/property-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.7", + "@smithy/smithy-client": "^3.4.4", + "@smithy/types": "^3.7.1", + "@smithy/util-stream": "^3.3.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.699.0.tgz", + "integrity": "sha512-dXmCqjJnKmG37Q+nLjPVu22mNkrGHY8hYoOt3Jo9R2zr5MYV7s/NHsCHr+7E+BZ+tfZYLRPeB1wkpTeHiEcdRw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/credential-provider-env": "3.696.0", + "@aws-sdk/credential-provider-http": "3.696.0", + "@aws-sdk/credential-provider-process": "3.696.0", + "@aws-sdk/credential-provider-sso": "3.699.0", + "@aws-sdk/credential-provider-web-identity": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.699.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.699.0.tgz", + "integrity": "sha512-MmEmNDo1bBtTgRmdNfdQksXu4uXe66s0p1hi1YPrn1h59Q605eq/xiWbGL6/3KdkViH6eGUuABeV2ODld86ylg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.696.0", + "@aws-sdk/credential-provider-http": "3.696.0", + "@aws-sdk/credential-provider-ini": "3.699.0", + "@aws-sdk/credential-provider-process": "3.696.0", + "@aws-sdk/credential-provider-sso": "3.699.0", + "@aws-sdk/credential-provider-web-identity": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/credential-provider-imds": "^3.2.6", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.696.0.tgz", + "integrity": "sha512-mL1RcFDe9sfmyU5K1nuFkO8UiJXXxLX4JO1gVaDIOvPqwStpUAwi3A1BoeZhWZZNQsiKI810RnYGo0E0WB/hUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.699.0.tgz", + "integrity": "sha512-Ekp2cZG4pl9D8+uKWm4qO1xcm8/MeiI8f+dnlZm8aQzizeC+aXYy9GyoclSf6daK8KfRPiRfM7ZHBBL5dAfdMA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.696.0", + "@aws-sdk/core": "3.696.0", + "@aws-sdk/token-providers": "3.699.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.696.0.tgz", + "integrity": "sha512-XJ/CVlWChM0VCoc259vWguFUjJDn/QwDqHwbx+K9cg3v6yrqXfK5ai+p/6lx0nQpnk4JzPVeYYxWRpaTsGC9rg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.696.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.696.0.tgz", + "integrity": "sha512-zELJp9Ta2zkX7ELggMN9qMCgekqZhFC5V2rOr4hJDEb/Tte7gpfKSObAnw/3AYiVqt36sjHKfdkoTsuwGdEoDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-logger": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.696.0.tgz", + "integrity": "sha512-KhkHt+8AjCxcR/5Zp3++YPJPpFQzxpr+jmONiT/Jw2yqnSngZ0Yspm5wGoRx2hS1HJbyZNuaOWEGuJoxLeBKfA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.696.0.tgz", + "integrity": "sha512-si/maV3Z0hH7qa99f9ru2xpS5HlfSVcasRlNUXKSDm611i7jFMWwGNLUOXFAOLhXotPX5G3Z6BLwL34oDeBMug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.696.0.tgz", + "integrity": "sha512-Lvyj8CTyxrHI6GHd2YVZKIRI5Fmnugt3cpJo0VrKKEgK5zMySwEZ1n4dqPK6czYRWKd5+WnYHYAuU+Wdk6Jsjw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@aws-sdk/util-endpoints": "3.696.0", + "@smithy/core": "^2.5.3", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.696.0.tgz", + "integrity": "sha512-7EuH142lBXjI8yH6dVS/CZeiK/WZsmb/8zP6bQbVYpMrppSTgB3MzZZdxVZGzL5r8zPQOU10wLC4kIMy0qdBVQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.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.10", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/token-providers": { + "version": "3.699.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.699.0.tgz", + "integrity": "sha512-kuiEW9DWs7fNos/SM+y58HCPhcIzm1nEZLhe2/7/6+TvAYLuEWURYsbK48gzsxXlaJ2k/jGY3nIsA7RptbMOwA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/property-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.10", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.699.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/types": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.696.0.tgz", + "integrity": "sha512-9rTvUJIAj5d3//U5FDPWGJ1nFJLuWb30vugGOrWk7aNZ6y9tuA3PI7Cc9dP8WEXKVyK1vuuk8rSFP2iqXnlgrw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-endpoints": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.696.0.tgz", + "integrity": "sha512-T5s0IlBVX+gkb9g/I6CLt4yAZVzMSiGnbUqWihWsHvQR1WOoIcndQy/Oz/IJXT9T2ipoy7a80gzV6a5mglrioA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "@smithy/util-endpoints": "^2.1.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.696.0.tgz", + "integrity": "sha512-Z5rVNDdmPOe6ELoM5AhF/ja5tSjbe6ctSctDPb0JdDf4dT0v2MfwhJKzXju2RzX8Es/77Glh7MlaXLE0kCB9+Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.696.0", + "@smithy/types": "^3.7.1", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.696.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.696.0.tgz", + "integrity": "sha512-KhKqcfyXIB0SCCt+qsu4eJjsfiOrNzK5dCV7RAW2YIpp+msxGUUX0NdRE9rkzjiv+3EMktgJm3eEIS+yxtlVdQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.696.0", + "@aws-sdk/types": "3.696.0", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-lambda/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.1.tgz", + "integrity": "sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==", + "dev": true, + "license": "Apache-2.0", + "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.2" + } + }, "node_modules/@aws-sdk/client-s3": { "version": "3.632.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.632.0.tgz", - "integrity": "sha512-GlhLo/t/E9w3EG3ZhFsSeSM3op2lfhc1LrLMXHFsmH1xIPkjIoxV+aHYIenuF7b/MGgMQDOjf07QeQNXfAr1gg==", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.632.0.tgz", + "integrity": "sha512-GlhLo/t/E9w3EG3ZhFsSeSM3op2lfhc1LrLMXHFsmH1xIPkjIoxV+aHYIenuF7b/MGgMQDOjf07QeQNXfAr1gg==", + "dev": true, + "license": "Apache-2.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.632.0", + "@aws-sdk/client-sts": "3.632.0", + "@aws-sdk/core": "3.629.0", + "@aws-sdk/credential-provider-node": "3.632.0", + "@aws-sdk/middleware-bucket-endpoint": "3.620.0", + "@aws-sdk/middleware-expect-continue": "3.620.0", + "@aws-sdk/middleware-flexible-checksums": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-location-constraint": "3.609.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-sdk-s3": "3.629.0", + "@aws-sdk/middleware-ssec": "3.609.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/signature-v4-multi-region": "3.629.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@aws-sdk/xml-builder": "3.609.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.2", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@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.14", + "@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.1.12", + "@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.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-secrets-manager": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.632.0.tgz", + "integrity": "sha512-WsQhPHHK1yPfALcP1B7nBSGDzky6vFTUEXnUdfzb5Xy2cT+JTBTS6ChtQGqqOuGHDP/3t/9soqZ+L6rUCYBb/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.632.0", + "@aws-sdk/client-sts": "3.632.0", + "@aws-sdk/core": "3.629.0", + "@aws-sdk/credential-provider-node": "3.632.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.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.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.3.2", + "@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.14", + "@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.1.12", + "@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.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@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.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sfn": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sfn/-/client-sfn-3.632.0.tgz", + "integrity": "sha512-LPaMqALuArGX1Y5N3tHX4lNlZabsqgfGJZjwyLTHawqiGNDMlMqOUg4Dl0e2Ies8yvMjs//YXtMWz26x24cGkQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.632.0", + "@aws-sdk/client-sts": "3.632.0", + "@aws-sdk/core": "3.629.0", + "@aws-sdk/credential-provider-node": "3.632.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.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.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.3.2", + "@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.14", + "@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.1.12", + "@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.14", + "@smithy/util-defaults-mode-node": "^3.0.14", + "@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.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sns": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sns/-/client-sns-3.632.0.tgz", + "integrity": "sha512-I8S0tFx26LL59/PiL4Reqna+LW7oIbRB+jygl4vrf4XIvpX5o4tmZmOmR0caYA+Cks3AomCi4cp+7WuwUpuATQ==", "dev": true, "license": "Apache-2.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.632.0", "@aws-sdk/client-sts": "3.632.0", "@aws-sdk/core": "3.629.0", "@aws-sdk/credential-provider-node": "3.632.0", - "@aws-sdk/middleware-bucket-endpoint": "3.620.0", - "@aws-sdk/middleware-expect-continue": "3.620.0", - "@aws-sdk/middleware-flexible-checksums": "3.620.0", "@aws-sdk/middleware-host-header": "3.620.0", - "@aws-sdk/middleware-location-constraint": "3.609.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", - "@aws-sdk/middleware-sdk-s3": "3.629.0", - "@aws-sdk/middleware-ssec": "3.609.0", "@aws-sdk/middleware-user-agent": "3.632.0", "@aws-sdk/region-config-resolver": "3.614.0", - "@aws-sdk/signature-v4-multi-region": "3.629.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.632.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", - "@aws-sdk/xml-builder": "3.609.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", - "@smithy/eventstream-serde-browser": "^3.0.6", - "@smithy/eventstream-serde-config-resolver": "^3.0.3", - "@smithy/eventstream-serde-node": "^3.0.5", "@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.14", @@ -674,9 +1412,7 @@ "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", - "@smithy/util-stream": "^3.1.3", "@smithy/util-utf8": "^3.0.0", - "@smithy/util-waiter": "^3.1.2", "tslib": "^2.6.2" }, "engines": { @@ -5544,12 +6280,13 @@ "license": "(Unlicense OR Apache-2.0)" }, "node_modules/@smithy/abort-controller": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.4.tgz", - "integrity": "sha512-VupaALAQlXViW3/enTf/f5l5JZYSAxoJL7f0nanhNNKnww6DGCg1oYIuNP78KDugnkwthBO6iEcym16HhWV8RQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.8.tgz", + "integrity": "sha512-+3DOBcUn5/rVjlxGvUPKc416SExarAQ+Qe0bqk30YSUjbepwpS7QN0cyKUSifvLJhdMZ0WPzPP5ymut0oonrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5578,15 +6315,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.8.tgz", - "integrity": "sha512-Tv1obAC18XOd2OnDAjSWmmthzx6Pdeh63FbLin8MlPiuJ2ATpKkq0NcNOJFr0dO+JmZXnwu8FQxKJ3TKJ3Hulw==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.12.tgz", + "integrity": "sha512-YAJP9UJFZRZ8N+UruTeq78zkdjUHmzsY62J4qKWZ4SXB4QXJ/+680EfXXgkYA2xj77ooMqtUY9m406zGNqwivQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.10", "tslib": "^2.6.2" }, "engines": { @@ -5594,19 +6332,18 @@ } }, "node_modules/@smithy/core": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.3.tgz", - "integrity": "sha512-4LTusLqFMRVQUfC3RNuTg6IzYTeJNpydRdTKq7J5wdEyIRQSu3rGIa3s80mgG2hhe6WOZl9IqTSo1pgbn6EHhA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.4.tgz", + "integrity": "sha512-iFh2Ymn2sCziBRLPuOOxRPkuCx/2gBdXtBGuCUFLUe6bWYjKnhHyIPqGeNkLZ5Aco/5GjebRTBFiWID3sDbrKw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-retry": "^3.0.18", - "@smithy/middleware-serde": "^3.0.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", + "@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.6", + "@smithy/util-middleware": "^3.0.10", + "@smithy/util-stream": "^3.3.1", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -5615,15 +6352,16 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.3.tgz", - "integrity": "sha512-VoxMzSzdvkkjMJNE38yQgx4CfnmT+Z+5EUXkg4x7yag93eQkVQgZvN3XBSHC/ylfBbLbAtdu7flTCChX9I+mVg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.7.tgz", + "integrity": "sha512-cEfbau+rrWF8ylkmmVAObOmjbTIzKyUC5TkBL58SbLywD0RCBC4JAUKbmtSm2w5KUJNRPGgpGFMvE2FKnuNlWQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", + "@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.2" }, "engines": { @@ -5631,25 +6369,27 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.5.tgz", - "integrity": "sha512-6pu+PT2r+5ZnWEV3vLV1DzyrpJ0TmehQlniIDCSpZg6+Ji2SfOI38EqUyQ+O8lotVElCrfVc9chKtSMe9cmCZQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.9.tgz", + "integrity": "sha512-F574nX0hhlNOjBnP+noLtsPFqXnWh2L0+nZKCwcu7P7J8k+k+rdIDs+RMnrMwrzhUE4mwMgyN0cYnEn0G8yrnQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.9.tgz", - "integrity": "sha512-PiQLo6OQmZAotJweIcObL1H44gkvuJACKMNqpBBe5Rf2Ax1DOcGi/28+feZI7yTe1ERHlQQaGnm8sSkyDUgsMg==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.13.tgz", + "integrity": "sha512-Nee9m+97o9Qj6/XeLz2g2vANS2SZgAxV4rDBMKGHvFJHU/xz88x2RwCkwsvEwYjSX4BV1NG1JXmxEaDUzZTAtw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.8", - "@smithy/types": "^3.4.2", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5657,12 +6397,13 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.6.tgz", - "integrity": "sha512-iew15It+c7WfnVowWkt2a7cdPp533LFJnpjDQgfZQcxv2QiOcyEcea31mnrk5PVbgo0nNH3VbYGq7myw2q/F6A==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.10.tgz", + "integrity": "sha512-K1M0x7P7qbBUKB0UWIL5KOcyi6zqV5mPJoL0/o01HPJr0CSq3A9FYuJC6e11EX6hR8QTIR++DBiGrYveOu6trw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5670,13 +6411,14 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.8.tgz", - "integrity": "sha512-6m+wI+fT0na+6oao6UqALVA38fsScCpoG5UO/A8ZSyGLnPM2i4MS1cFUhpuALgvLMxfYoTCh7qSeJa0aG4IWpQ==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.12.tgz", + "integrity": "sha512-kiZymxXvZ4tnuYsPSMUHe+MMfc4FTeFWJIc0Q5wygJoUQM4rVHNghvd48y7ppuulNMbuYt95ah71pYc2+o4JOA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^3.0.8", - "@smithy/types": "^3.4.2", + "@smithy/eventstream-serde-universal": "^3.0.12", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5684,13 +6426,14 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.8.tgz", - "integrity": "sha512-09tqzIQ6e+7jLqGvRji1yJoDbL/zob0OFhq75edgStWErGLf16+yI5hRc/o9/YAybOhUZs/swpW2SPn892G5Gg==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.12.tgz", + "integrity": "sha512-1i8ifhLJrOZ+pEifTlF0EfZzMLUGQggYQ6WmZ4d5g77zEKf7oZ0kvh1yKWHPjofvOwqrkwRDVuxuYC8wVd662A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^3.1.5", - "@smithy/types": "^3.4.2", + "@smithy/eventstream-codec": "^3.1.9", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5724,12 +6467,13 @@ } }, "node_modules/@smithy/hash-node": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.6.tgz", - "integrity": "sha512-c/FHEdKK/7DU2z6ZE91L36ahyXWayR3B+FzELjnYq7wH5YqIseM24V+pWCS9kFn1Ln8OFGTf+pyYPiHZuX0s/Q==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.10.tgz", + "integrity": "sha512-3zWGWCHI+FlJ5WJwx73Mw2llYR8aflVyZN5JhoqLxbdPZi6UyKSdCeXAWJw9ja22m6S6Tzz1KZ+kAaSwvydi0g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -5754,12 +6498,13 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.6.tgz", - "integrity": "sha512-czM7Ioq3s8pIXht7oD+vmgy4Wfb4XavU/k/irO8NdXFFOx7YAlsCCcKOh/lJD1mJSYQqiR7NmpZ9JviryD/7AQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.10.tgz", + "integrity": "sha512-Lp2L65vFi+cj0vFMu2obpPW69DU+6O5g3086lmI4XcnRCG8PxvpWC7XyaVwJCxsZFzueHjXnrOH/E0pl0zikfA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" } }, @@ -5789,13 +6534,14 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.8.tgz", - "integrity": "sha512-VuyszlSO49WKh3H9/kIO2kf07VUwGV80QRiaDxUfP8P8UKlokz381ETJvwLhwuypBYhLymCYyNhB3fLAGBX2og==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.12.tgz", + "integrity": "sha512-1mDEXqzM20yywaMDuf5o9ue8OkJ373lSPbaSjyEvkWdqELhFMyNNgKGWL/rCSf4KME8B+HlHKuR8u9kRj8HzEQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5803,17 +6549,19 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.3.tgz", - "integrity": "sha512-KeM/OrK8MVFUsoJsmCN0MZMVPjKKLudn13xpgwIMpGTYpA8QZB2Xq5tJ+RE6iu3A6NhOI4VajDTwBsm8pwwrhg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.4.tgz", + "integrity": "sha512-TybiW2LA3kYVd3e+lWhINVu1o26KJbBwOpADnf0L4x/35vLVica77XVR5hvV9+kWeTGeSJ3IHTcYxbRxlbwhsg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.6", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", - "@smithy/url-parser": "^3.0.6", - "@smithy/util-middleware": "^3.0.6", + "@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.2" }, "engines": { @@ -5821,18 +6569,19 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.18.tgz", - "integrity": "sha512-YU1o/vYob6vlqZdd97MN8cSXRToknLXhFBL3r+c9CZcnxkO/rgNZ++CfgX2vsmnEKvlqdi26+SRtSzlVp5z6Mg==", + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.28.tgz", + "integrity": "sha512-vK2eDfvIXG1U64FEUhYxoZ1JSj4XFbYWkK36iz02i3pFwWiDz1Q7jKhGTBCwx/7KqJNk4VS7d7cDLXFOvP7M+g==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/protocol-http": "^4.1.3", - "@smithy/service-error-classification": "^3.0.6", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", - "@smithy/util-middleware": "^3.0.6", - "@smithy/util-retry": "^3.0.6", + "@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.2", "uuid": "^9.0.1" }, @@ -5841,12 +6590,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.6.tgz", - "integrity": "sha512-KKTUSl1MzOM0MAjGbudeaVNtIDo+PpekTBkCNwvfZlKndodrnvRo+00USatiyLOc0ujjO9UydMRu3O9dYML7ag==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.10.tgz", + "integrity": "sha512-MnAuhh+dD14F428ubSJuRnmRsfOpxSzvRhaGVTvd/lrUDE3kxzCCmH8lnVTvoNQnV2BbJ4c15QwZ3UdQBtFNZA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5854,12 +6604,13 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.6.tgz", - "integrity": "sha512-2c0eSYhTQ8xQqHMcRxLMpadFbTXg6Zla5l0mwNftFCZMQmuhI7EbAJMx6R5eqfuV3YbJ3QGyS3d5uSmrHV8Khg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.10.tgz", + "integrity": "sha512-grCHyoiARDBBGPyw2BeicpjgpsDFWZZxptbVKb3CRd/ZA15F/T6rZjCCuBUjJwdck1nwUuIxYtsS4H9DDpbP5w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5867,14 +6618,15 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.7.tgz", - "integrity": "sha512-g3mfnC3Oo8pOI0dYuPXLtdW1WGVb3bR2tkV21GNkm0ZvQjLTtamXAwCWt/FCb0HGvKt3gHHmF1XerG0ICfalOg==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.11.tgz", + "integrity": "sha512-URq3gT3RpDikh/8MBJUB+QGZzfS7Bm6TQTqoh4CqE8NBuyPkWa5eUXj0XFcFfeZVgg3WMh1u19iaXn8FvvXxZw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.6", - "@smithy/shared-ini-file-loader": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/property-provider": "^3.1.10", + "@smithy/shared-ini-file-loader": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5882,15 +6634,16 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.2.tgz", - "integrity": "sha512-42Cy4/oT2O+00aiG1iQ7Kd7rE6q8j7vI0gFfnMlUiATvyo8vefJkhb7O10qZY0jAqo5WZdUzfl9IV6wQ3iMBCg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.3.1.tgz", + "integrity": "sha512-fr+UAOMGWh6bn4YSEezBCpJn9Ukp9oR4D32sCjCo7U81evE11YePOQ58ogzyfgmjIO79YeOdfXXqr0jyhPQeMg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.4", - "@smithy/protocol-http": "^4.1.3", - "@smithy/querystring-builder": "^3.0.6", - "@smithy/types": "^3.4.2", + "@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.2" }, "engines": { @@ -5898,12 +6651,13 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.6.tgz", - "integrity": "sha512-NK3y/T7Q/Bw+Z8vsVs9MYIQ5v7gOX7clyrXcwhhIBQhbPgRl6JDrZbusO9qWDhcEus75Tg+VCxtIRfo3H76fpw==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.10.tgz", + "integrity": "sha512-n1MJZGTorTH2DvyTVj+3wXnd4CzjJxyXeOgnTlgNVFxaaMeT4OteEp4QrzF8p9ee2yg42nvyVK6R/awLCakjeQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5911,12 +6665,13 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.3.tgz", - "integrity": "sha512-GcbMmOYpH9iRqtC05RbRnc/0FssxSTHlmaNhYBTgSgNCYpdR3Kt88u5GAZTBmouzv+Zlj/VRv92J9ruuDeJuEw==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.7.tgz", + "integrity": "sha512-FP2LepWD0eJeOTm0SjssPcgqAlDFzOmRXqXmGhfIM52G7Lrox/pcpQf6RP4F21k0+O12zaqQt5fCDOeBtqY6Cg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5924,12 +6679,13 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.6.tgz", - "integrity": "sha512-sQe08RunoObe+Usujn9+R2zrLuQERi3CWvRO3BvnoWSYUaIrLKuAIeY7cMeDax6xGyfIP3x/yFWbEKSXvOnvVg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.10.tgz", + "integrity": "sha512-nT9CQF3EIJtIUepXQuBFb8dxJi3WVZS3XfuDksxSCSn+/CzZowRLdhDn+2acbBv8R6eaJqPupoI/aRFIImNVPQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -5938,12 +6694,13 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.6.tgz", - "integrity": "sha512-UJKw4LlEkytzz2Wq+uIdHf6qOtFfee/o7ruH0jF5I6UAuU+19r9QV7nU3P/uI0l6+oElRHmG/5cBBcGJrD7Ozg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.10.tgz", + "integrity": "sha512-Oa0XDcpo9SmjhiDD9ua2UyM3uU01ZTuIrNdZvzwUTykW1PM8o2yJvMh1Do1rY5sUQg4NDV70dMi0JhDx4GyxuQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5951,24 +6708,26 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.6.tgz", - "integrity": "sha512-53SpchU3+DUZrN7J6sBx9tBiCVGzsib2e4sc512Q7K9fpC5zkJKs6Z9s+qbMxSYrkEkle6hnMtrts7XNkMJJMg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.10.tgz", + "integrity": "sha512-zHe642KCqDxXLuhs6xmHVgRwy078RfqxP2wRDpIyiF8EmsWXptMwnMwbVa50lw+WOGNrYm9zbaEg0oDe3PTtvQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2" + "@smithy/types": "^3.7.1" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.7.tgz", - "integrity": "sha512-IA4K2qTJYXkF5OfVN4vsY1hfnUZjaslEE8Fsr/gGFza4TAC2A9NfnZuSY2srQIbt9bwtjHiAayrRVgKse4Q7fA==", + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.11.tgz", + "integrity": "sha512-AUdrIZHFtUgmfSN4Gq9nHu3IkHMa1YDcN+s061Nfm+6pQ0mJy85YQDB0tZBCmls0Vuj22pLwDPmL92+Hvfwwlg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -5976,16 +6735,17 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.3.tgz", - "integrity": "sha512-YD2KYSCEEeFHcWZ1E3mLdAaHl8T/TANh6XwmocQ6nPcTdBfh4N5fusgnblnWDlnlU1/cUqEq3PiGi22GmT2Lkg==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.3.tgz", + "integrity": "sha512-pPSQQ2v2vu9vc8iew7sszLd0O09I5TRc5zhY71KA+Ao0xYazIG+uLeHbTJfIWGO3BGVLiXjUr3EEeCcEQLjpWQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", + "@smithy/protocol-http": "^4.1.7", + "@smithy/types": "^3.7.1", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.6", + "@smithy/util-middleware": "^3.0.10", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -5995,16 +6755,18 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.3.2.tgz", - "integrity": "sha512-RKDfhF2MTwXl7jan5d7QfS9eCC6XJbO3H+EZAvLQN8A5in4ib2Ml4zoeLo57w9QrqFekBPcsoC2hW3Ekw4vQ9Q==", + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.5.tgz", + "integrity": "sha512-k0sybYT9zlP79sIKd1XGm4TmK0AS1nA2bzDHXx7m0nGi3RQ8dxxQUs4CPkSmQTKAo+KF9aINU3KzpGIpV7UoMw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.3", - "@smithy/middleware-stack": "^3.0.6", - "@smithy/protocol-http": "^4.1.3", - "@smithy/types": "^3.4.2", - "@smithy/util-stream": "^3.1.6", + "@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.2" }, "engines": { @@ -6012,10 +6774,11 @@ } }, "node_modules/@smithy/types": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.4.2.tgz", - "integrity": "sha512-tHiFcfcVedVBHpmHUEUHOCCih8iZbIAYn9NvPsNzaPm/237I3imdDdZoOC8c87H5HBAVEa06tTgb+OcSWV9g5w==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.7.1.tgz", + "integrity": "sha512-XKLcLXZY7sUQgvvWyeaL/qwNPp6V3dWcUjqrQKjSb+tzYiCy340R/c64LV5j+Tnb2GhmunEX0eou+L+m2hJNYA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -6024,13 +6787,14 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.6.tgz", - "integrity": "sha512-47Op/NU8Opt49KyGpHtVdnmmJMsp2hEwBdyjuFB9M2V5QVOwA7pBhhxKN5z6ztKGrMw76gd8MlbPuzzvaAncuQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.10.tgz", + "integrity": "sha512-j90NUalTSBR2NaZTuruEgavSdh8MLirf58LoGSk4AtQfyIymogIhgnGUU2Mga2bkMkpSoC9gxb74xBXL5afKAQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/querystring-parser": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" } }, @@ -6100,14 +6864,15 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.18.tgz", - "integrity": "sha512-/eveCzU6Z6Yw8dlYQLA4rcK30XY0E4L3lD3QFHm59mzDaWYelrXE1rlynuT3J6qxv+5yNy3a1JuzhG5hk5hcmw==", + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.28.tgz", + "integrity": "sha512-6bzwAbZpHRFVJsOztmov5PGDmJYsbNSoIEfHSJJyFLzfBGCCChiO3od9k7E/TLgrCsIifdAbB9nqbVbyE7wRUw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.6", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", + "@smithy/property-provider": "^3.1.10", + "@smithy/smithy-client": "^3.4.5", + "@smithy/types": "^3.7.1", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -6116,17 +6881,18 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.18.tgz", - "integrity": "sha512-9cfzRjArtOFPlTYRREJk00suUxVXTgbrzVncOyMRTUeMKnecG/YentLF3cORa+R6mUOMSrMSnT18jos1PKqK6Q==", + "version": "3.0.28", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.28.tgz", + "integrity": "sha512-78ENJDorV1CjOQselGmm3+z7Yqjj5HWCbjzh0Ixuq736dh1oEnD9sAttSBNSLlpZsX8VQnmERqA2fEFlmqWn8w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.8", - "@smithy/credential-provider-imds": "^3.2.3", - "@smithy/node-config-provider": "^3.1.7", - "@smithy/property-provider": "^3.1.6", - "@smithy/smithy-client": "^3.3.2", - "@smithy/types": "^3.4.2", + "@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.2" }, "engines": { @@ -6134,13 +6900,14 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.2.tgz", - "integrity": "sha512-FEISzffb4H8DLzGq1g4MuDpcv6CIG15fXoQzDH9SjpRJv6h7J++1STFWWinilG0tQh9H1v2UKWG19Jjr2B16zQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.6.tgz", + "integrity": "sha512-mFV1t3ndBh0yZOJgWxO9J/4cHZVn5UG1D8DeCc6/echfNkeEJWu9LD7mgGH5fHrEdR7LDoWw7PQO6QiGpHXhgA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.7", - "@smithy/types": "^3.4.2", + "@smithy/node-config-provider": "^3.1.11", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6161,12 +6928,13 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.6.tgz", - "integrity": "sha512-BxbX4aBhI1O9p87/xM+zWy0GzT3CEVcXFPBRDoHAM+pV0eSW156pR+PSYEz0DQHDMYDsYAflC2bQNz2uaDBUZQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.10.tgz", + "integrity": "sha512-eJO+/+RsrG2RpmY68jZdwQtnfsxjmPxzMlQpnHKjFPwrYqvlcT+fHdT+ZVwcjlWSrByOhGr9Ff2GG17efc192A==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.4.2", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6174,13 +6942,14 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.6.tgz", - "integrity": "sha512-BRZiuF7IwDntAbevqMco67an0Sr9oLQJqqRCsSPZZHYRnehS0LHDAkJk/pSmI7Z8c/1Vet294H7fY2fWUgB+Rg==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.10.tgz", + "integrity": "sha512-1l4qatFp4PiU6j7UsbasUHL2VU023NRB/gfaa1M0rDqVrRN4g3mCArLRyH3OuktApA4ye+yjWQHjdziunw2eWA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.6", - "@smithy/types": "^3.4.2", + "@smithy/service-error-classification": "^3.0.10", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": { @@ -6188,14 +6957,15 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.6.tgz", - "integrity": "sha512-lQEUfTx1ht5CRdvIjdAN/gUL6vQt2wSARGGLaBHNe+iJSkRHlWzY+DOn0mFTmTgyU3jcI5n9DkT5gTzYuSOo6A==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.3.1.tgz", + "integrity": "sha512-Ff68R5lJh2zj+AUTvbAU/4yx+6QPRzg7+pI7M1FbtQHcRIp7xvguxVsQBKyB3fwiOwhAKu0lnNyYBaQfSW6TNw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.7", - "@smithy/node-http-handler": "^3.2.2", - "@smithy/types": "^3.4.2", + "@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", @@ -6206,6 +6976,20 @@ "node": ">=16.0.0" } }, + "node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.1.tgz", + "integrity": "sha512-bH7QW0+JdX0bPBadXt8GwMof/jz0H28I84hU1Uet9ISpzUqXqRQ3fEZJ+ANPOhzSEczYvANNl3uDQDYArSFDtA==", + "dev": true, + "license": "Apache-2.0", + "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.2" + } + }, "node_modules/@smithy/util-uri-escape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", @@ -6234,14 +7018,14 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.5.tgz", - "integrity": "sha512-jYOSvM3H6sZe3CHjzD2VQNCjWBJs+4DbtwBMvUp9y5EnnwNa7NQxTeYeQw0CKCAdGGZ3QvVkyJmvbvs5M/B10A==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.9.tgz", + "integrity": "sha512-/aMXPANhMOlMPjfPtSrDfPeVP8l56SJlz93xeiLmhLe5xvlXA5T3abZ2ilEsDEPeY9T/wnN/vNGn9wa1SbufWA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.4", - "@smithy/types": "^3.4.2", + "@smithy/abort-controller": "^3.1.8", + "@smithy/types": "^3.7.1", "tslib": "^2.6.2" }, "engines": {